openvibers/openvibe.network Commits

Front door: every icon drawn inline (the page has no icon font) (#836)

OP OpenVibers committed 2 days ago · ab823969a1 · 3 files, +12 −13

Network's home loads no icon font, so its fa-* icons were blank: the five popular rows' marks (shipped in #834), the
rising arrow, the play disc on every recording and screenshot card, the rails' chevrons and the Recordings and
Screenshots marks. They are now the shared icons' inline SVG (the sites' own marks via icon()) and three small drawn
SVGs (play, chevron, rising). Perf budget: HTML 218 KB (measured 217: Directory in the list on contracts 0.406.0 and
the inline marks; main was already over 210.5).

Changes

ddiff --git a/server/home/render.js b/server/home/render.js
iindex 3e13f26b..8460c14a 100644
---- a/server/home/render.js
++++ b/server/home/render.js
@@@ -150,13 +150,13 @@ function rightNow(a) {
const views = (n) => (n ? `${fmtViewers(n)} view${n === 1 ? '' : 's'}` : '');
const face = (src) => (src ? img(src, null, 36, 36) : '<span class="vc-noface"></span>');
const card = ({ url, thumb, badge, len, about, avatar, title, who, line, cls = '' }) => `<li><a class="vcard${cls}" href="${esc(url)}">
--<span class="vc-thumb">${thumb ? img(thumb, avatar, 320, 180) : '<span class="now-noimg"></span>'}${badge || ''}${len ? `<span class="vc-len">${esc(len)}</span>` : ''}<span class="vc-play" aria-hidden="true"><i class="fa-solid fa-play"></i></span>${about ? `<span class="vc-about">${esc(about)}</span>` : ''}</span>
++<span class="vc-thumb">${thumb ? img(thumb, avatar, 320, 180) : '<span class="now-noimg"></span>'}${badge || ''}${len ? `<span class="vc-len">${esc(len)}</span>` : ''}<span class="vc-play" aria-hidden="true"><i>${PLAY}</i></span>${about ? `<span class="vc-about">${esc(about)}</span>` : ''}</span>
<span class="vc-meta">${face(avatar)}<span class="vc-text"><b>${esc(title)}</b>${who ? `<small>${esc(who)}</small>` : ''}${line ? `<small>${esc(line)}</small>` : ''}</span></span></a></li>`;
// A block's head (ask 364, 2026-11-11: the owner's "overhaul … more organized"): its name in sentence case with
// its own mark and how many, and one short link to the rest (its full name in the title) that never wraps.
const head = (title, n, href, more, short, mark) => `<div class="now-head"><h3 class="now-h">${mark}<span>${title}</span>${n ? `<em class="now-n">${n}</em>` : ''}</h3>`
-- + `<a class="now-more" href="${esc(href)}" title="${esc(more)}" aria-label="${esc(more)}">${esc(short)}<i class="fa-solid fa-chevron-right" aria-hidden="true"></i></a></div>`;
-- const MARK = { live: '<span class="now-h-live" aria-hidden="true"></span>', recs: '<i class="fa-solid fa-film now-h-ic" aria-hidden="true"></i>', shots: '<i class="fa-solid fa-image now-h-ic" aria-hidden="true"></i>' };
++ + `<a class="now-more" href="${esc(href)}" title="${esc(more)}" aria-label="${esc(more)}">${esc(short)}<i>${CHEVRON}</i></a></div>`;
++ const MARK = { live: '<span class="now-h-live" aria-hidden="true"></span>', recs: `<span class="now-h-ic">${icon('video', 16)}</span>`, shots: `<span class="now-h-ic">${icon('image', 16)}</span>` };
const rail = (title, href, more, cards, id = '', short = 'All', mark = MARK.recs) => (cards.length ? `<div class="now-block${cards.length === 1 ? ' is-one' : ''}"${id ? ` id="${id}"` : ''}>${head(title, cards.length, href, more, short, mark)}<ul class="now-rail" data-ovk-scroll>${cards.join('')}</ul></div>` : '');
const live = a.live.map((s) => card({ url: s.url, thumb: s.thumb || s.avatar, badge: `<span class="now-badge"><i></i>LIVE${s.viewers ? ` · ${esc(fmtViewers(s.viewers))}` : ''}</span>`, avatar: s.avatar, title: s.title, who: s.name, line: s.category }));
@@@ -209,21 +209,20 @@ function moreParts(parts) {
}
// The five things people do now (plan L43; designed by Opus 2026-11-11): a ranked list, each row its place, the site's
--// mark, the reason Report gives and the number behind it (growth when it is rising, else the week's opens; a number
++// own mark, the reason Report gives and the number behind it (growth when it is rising, else the week's opens; a number
// Report did not send is left out). The order and the numbers are Report's; when Report does not answer the fixed
// starter list is used and the section says so in data-source. A phone reads it as rows, a computer as five cards.
--const POP_ICON = { 'media-hub': 'fa-play', video: 'fa-play', tours: 'fa-route', community: 'fa-users', news: 'fa-newspaper',
-- design: 'fa-palette', report: 'fa-chart-column', network: 'fa-circle-nodes', music: 'fa-music', trade: 'fa-chart-line',
-- coupons: 'fa-ticket', deals: 'fa-tags', download: 'fa-folder', cam: 'fa-camera', openre: 'fa-share-nodes', host: 'fa-server',
-- reviews: 'fa-star', media: 'fa-photo-film', exchange: 'fa-arrow-right-arrow-left', compare: 'fa-scale-balanced' };
--const popIcon = (it) => POP_ICON[it.id] || POP_ICON[it.icon] || (INTENTS.find((x) => x[0] === it.id || x[0] === it.icon) || [])[1] || 'fa-circle-nodes';
++// This page draws no icon font: the marks are the shared icons' inline SVG (icon() above), and the rising arrow is drawn here.
++const PLAY = '<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M8 5.5v13l11-6.5z"/></svg>';
++const CHEVRON = '<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 5l7 7-7 7"/></svg>';
++const TREND_UP = '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 17l6-6 4 4 8-8"/><path d="M14 7h7v7"/></svg>';
/** The number behind a row: "+44%" or "35×" this week when it is rising, else the week's opens; '' when Report sent none. */
function popFigure(it) {
const g = Number(it.growth_7d_pct);
if (/rising/i.test(it.reason) && Number.isFinite(g) && g > 0) {
const big = g >= 200 && Number(it.opens_prev_7d) > 0;
const v = big ? `${Math.round(Number(it.opens_7d) / Number(it.opens_prev_7d))}×` : `+${g}%`;
-- return `<span class="pop-n is-up" title="${esc(big ? `${v} last week's opens` : `${v} on last week`)}"><i class="fa-solid fa-arrow-trend-up" aria-hidden="true"></i>${esc(v)}</span>`;
++ return `<span class="pop-n is-up" title="${esc(big ? `${v} last week's opens` : `${v} on last week`)}">${TREND_UP}${esc(v)}</span>`;
}
const n = Number(it.opens_7d);
return Number.isFinite(n) && n > 0 ? `<span class="pop-n" title="${esc(`${n.toLocaleString('en-US')} opens this week`)}">${esc(fmtVisits(n))}<small>this week</small></span>` : '';
@@@ -236,7 +235,7 @@ function popularNow() {
? "Ranked from OpenVibe.Report's numbers: what people opened today and this week."
: 'A fixed starter list while OpenVibe.Report is away.';
const row = (it, i) => `<li><a class="pop-row" href="${esc(it.url)}"><span class="pop-rank" aria-hidden="true">${i + 1}</span>`
-- + `<span class="pop-ic" aria-hidden="true"><i class="fa-solid ${esc(popIcon(it))}"></i></span>`
++ + `<span class="pop-ic" aria-hidden="true">${icon(it.icon || it.id, 20)}</span>`
+ `<span class="pop-t"><b>${esc(it.name)}</b><small>${esc(it.reason)}</small></span>${popFigure(it)}</a></li>`;
return `<section class="sc-sec pop" id="popular" data-source="${esc(p.source)}" aria-labelledby="h-popular"><h2 id="h-popular" class="ovk-titled">${sectionIcon('grid')}Five things people do now</h2><p class="sc-lede">${esc(lede)}</p><ol class="pop-list">${items.map(row).join('')}</ol></section>`;
}
ddiff --git a/test/home-front-door.test.js b/test/home-front-door.test.js
iindex 4aec3a6e..c70cf3b0 100644
---- a/test/home-front-door.test.js
++++ b/test/home-front-door.test.js
@@@ -94,7 +94,7 @@ assert.ok(/Go live<\/b><small>From a browser tab\. No OBS, no follower minimum\.
// The five most popular list (plan L43) is a plain list; with no Report wired in this process it says it is the floor.
assert.ok(/<section class="sc-sec pop" id="popular" data-source="fallback"/.test(html), 'the signed-out popularity list is on the page and names its source');
assert.ok(html.includes('Five things people do now') && /<ol class="pop-list">/.test(html), 'the five are a ranked list');
--assert.ok(/<a class="pop-row" href="https:\/\/openvibe\.live\/"><span class="pop-rank" aria-hidden="true">1<\/span><span class="pop-ic" aria-hidden="true"><i class="fa-solid fa-tower-broadcast"><\/i><\/span>/.test(html), 'each row: its place, the site\'s mark');
++assert.ok(/<a class="pop-row" href="https:\/\/openvibe\.live\/"><span class="pop-rank" aria-hidden="true">1<\/span><span class="pop-ic" aria-hidden="true"><span class="ov-icon" data-icon="live"/.test(html), 'each row: its place, the site\'s own mark (inline SVG: this page has no icon font)');
assert.ok(!/class="pop-n/.test(html.slice(html.indexOf('id="popular"'), html.indexOf('</ol>', html.indexOf('id="popular"')))), 'the fallback list has no numbers, so it shows none');
assert.ok(/Nobody is live right now/.test(html) && /Go live from your browser/.test(html), 'an honest empty state');
const open = sites.filter((s) => s.status === 'open' && s.id !== 'network');
ddiff --git a/test/perf-budget.test.js b/test/perf-budget.test.js
iindex daeb6126..beb83753 100644
---- a/test/perf-budget.test.js
++++ b/test/perf-budget.test.js
@@@ -12,7 +12,7 @@ const { spawn } = require('child_process');
const { measure, check, format } = require('openvibe-shared/perf-budget');
const BUDGETS = {
-- htmlRawKB: 210.5, // 210.5: measured 209.9 KB with the popular block designed (2026-11-11: ranks, marks, numbers and its CSS, +0.9 KB); 209.5: measured 209 KB with openvibe-shared 4.190.0 (2026-11-10: the site name's dropdown, the footer's online strip, the balances pill); 206: measured 205.8 KB with openvibe-shared 4.185.0 (the navbar's links in the first HTML since 4.184.0), 2026-11-10; 205.4: measured 205.4 KB with the front door's five most popular destinations (plan L43, 2026-11-09: Report's ranked list adds about 1.2 KB of plain list markup); 204.2: measured 204.2 KB with openvibe-contracts 0.373.0 (the Git service in the network list) on openvibe-shared 4.173.0, 2026-11-09; 204: measured 203.5 KB with the shared page head's kit CSS on the home (openvibe-shared 4.163.0 + page head 1369721), 2026-11-09; 198.8 with openvibe-contracts 0.348.0 (2026-11-08: Studio joins the menu; Studio and Locker get menu lines); 197.4 with Email open (contracts 0.330.0, 2026-11-07: the mail site in the network list); 196 with the Node's own item in the navbar (openvibe-shared 4.117.0, 2026-11-06: the Node link and the llms.txt section); 194.9 with the Organization naming every open site in sameAs (SEO-27, 2026-11-03); 192.4 with Tours and Shoes open (contracts 0.265.0, 2026-10-31); 189.9 with Casino open (contracts 0.264.0, 2026-10-31); 189 with the look-boot script inline above the stylesheets on every page (L42, 2026-10-31: +1 KB); 188 with the agent layer's rel=alternate/describedby link tags on every public page (SEO-1/SEO-2, 2026-10-29: +0.2 KB); 187 with Bench open (contracts 0.238.0, 2026-10-28); 186 with Cam open (contracts 0.237.0, 2026-10-28); 184.4 with Fun open (contracts 0.236.0, 2026-10-28); 182.7 with Vision and Exchange open (contracts 0.235.0, 2026-10-28); 179 with Meme open (contracts 0.234.0, 2026-10-28); 174.1 with Domains, Report and Design open (contracts 0.226-0.228, 2026-10-28); 168.1 the hero's orbit and rich cards for live streams, recordings and screenshots (2026-10-28: the owner asked for animated visuals and YouTube-style cards); 152.4 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 147.9 with the home's FAQ (six questions and their FAQPage JSON-LD), the numbers panel and the service map's group headings (2026-10-22); 141.2 with Tips and VIP open (contracts 0.200.0, 37 sites; the Opening next section is gone); 134 with Play (contracts 0.172.1); 128.3 with Events, AI and Search added (31 sites, contracts 0.135.0); 122.3 (shared 2.21.0: a real glyph for every product card, where ten drew the 'V'); was 110.6 with 33 live sites (contracts 0.119.0). The home page lists every site, so it grows with the network
++ htmlRawKB: 218, // 218: measured 217 KB (2026-11-11: Directory joins the network list on contracts 0.406.0, and the popular block draws its five site marks as inline SVG, this page having no icon font); 210.5: measured 209.9 KB with the popular block designed (2026-11-11: ranks, marks, numbers and its CSS, +0.9 KB); 209.5: measured 209 KB with openvibe-shared 4.190.0 (2026-11-10: the site name's dropdown, the footer's online strip, the balances pill); 206: measured 205.8 KB with openvibe-shared 4.185.0 (the navbar's links in the first HTML since 4.184.0), 2026-11-10; 205.4: measured 205.4 KB with the front door's five most popular destinations (plan L43, 2026-11-09: Report's ranked list adds about 1.2 KB of plain list markup); 204.2: measured 204.2 KB with openvibe-contracts 0.373.0 (the Git service in the network list) on openvibe-shared 4.173.0, 2026-11-09; 204: measured 203.5 KB with the shared page head's kit CSS on the home (openvibe-shared 4.163.0 + page head 1369721), 2026-11-09; 198.8 with openvibe-contracts 0.348.0 (2026-11-08: Studio joins the menu; Studio and Locker get menu lines); 197.4 with Email open (contracts 0.330.0, 2026-11-07: the mail site in the network list); 196 with the Node's own item in the navbar (openvibe-shared 4.117.0, 2026-11-06: the Node link and the llms.txt section); 194.9 with the Organization naming every open site in sameAs (SEO-27, 2026-11-03); 192.4 with Tours and Shoes open (contracts 0.265.0, 2026-10-31); 189.9 with Casino open (contracts 0.264.0, 2026-10-31); 189 with the look-boot script inline above the stylesheets on every page (L42, 2026-10-31: +1 KB); 188 with the agent layer's rel=alternate/describedby link tags on every public page (SEO-1/SEO-2, 2026-10-29: +0.2 KB); 187 with Bench open (contracts 0.238.0, 2026-10-28); 186 with Cam open (contracts 0.237.0, 2026-10-28); 184.4 with Fun open (contracts 0.236.0, 2026-10-28); 182.7 with Vision and Exchange open (contracts 0.235.0, 2026-10-28); 179 with Meme open (contracts 0.234.0, 2026-10-28); 174.1 with Domains, Report and Design open (contracts 0.226-0.228, 2026-10-28); 168.1 the hero's orbit and rich cards for live streams, recordings and screenshots (2026-10-28: the owner asked for animated visuals and YouTube-style cards); 152.4 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 147.9 with the home's FAQ (six questions and their FAQPage JSON-LD), the numbers panel and the service map's group headings (2026-10-22); 141.2 with Tips and VIP open (contracts 0.200.0, 37 sites; the Opening next section is gone); 134 with Play (contracts 0.172.1); 128.3 with Events, AI and Search added (31 sites, contracts 0.135.0); 122.3 (shared 2.21.0: a real glyph for every product card, where ten drew the 'V'); was 110.6 with 33 live sites (contracts 0.119.0). The home page lists every site, so it grows with the network
htmlBrotliKB: 39.5, // 39.5: measured 39.3 KB brotli with openvibe-shared 4.190.0 (2026-11-10: the site name's dropdown, the footer's online strip, the balances pill); 39: measured 38.6 KB brotli with the shared page head's kit CSS on the home (openvibe-shared 4.163.0 + page head 1369721), 2026-11-09; 38.1 with openvibe-contracts 0.348.0 (2026-11-08: Studio joins the menu; Studio and Locker get menu lines); 37.9 with openvibe-shared 4.129.0 (2026-11-07: the one menu (the burger opens the OpenVibe menu)); 37.8 with openvibe-shared 4.129.0 (2026-11-07: the one menu (the burger opens the OpenVibe menu)); 36.1 with the Node's own item in the navbar (openvibe-shared 4.117.0, 2026-11-06); 35.5 with Tours and Shoes open (2026-10-31); 35.1 with the look-boot script inline above the stylesheets on every page (L42, 2026-10-31: +0.6 KB); 34.2 with the lower half as one block of chips on phones (2026-10-30); 33.1 with Games granted its Bench match capabilities (BENCH-7, 2026-10-29: the home lists what each service may do); 32.0 with Fun, Vision and Exchange open (contracts 0.236.0, 2026-10-28); 31.2 with Domains, Report and Design open (2026-10-28); 30.1 with the Organization saying who OpenVibe is (description, other names, what it is not, 15 sites; 2026-10-28); 28.5 with the FAQ, numbers and groups (2026-10-22); 26.1 with Play; 25.1 (31 sites, contracts 0.135.0); 24.1 (shared 2.21.0, the same glyphs); 20.5 (contracts 0.116.0)
jsFiles: 8, // 7 with boost.js, was 6 (2026-10-13, plan T11 step 3: one more script on every boosted page; it loads route-transition.js on the first move only)
jsRawKB: 448.5, // 448.5 openvibe-shared v4.205.0 (2026-11-11: measured 448.4, raised by pin-bump from 445); 445 openvibe-shared v4.202.0 (2026-11-11: measured 444.6, raised by pin-bump from 444); 444 openvibe-shared v4.201.0 (2026-11-11: measured 443.7, raised by pin-bump from 440.5); 440.5 openvibe-shared v4.200.0 (2026-11-10: measured 440.2, raised by pin-bump from 440.0); 440.0: measured 439.6 KB raw / 106.7 KB brotli with openvibe-shared 4.197.0 (2026-11-10: the menu's row of places, the You tab's row, the Sign in mark, the tooltip); 438.5: measured 438 KB raw / 106.5 KB brotli with openvibe-shared 4.190.0 (2026-11-10: the site name's dropdown, the footer's online strip, the balances pill); 421: measured 420.9 KB raw / 102.2 KB brotli with openvibe-shared 4.186.0 (the mark's Working and Thinking states, the Dock and the menu as one sheet, bare controls), 2026-11-10; 420.5: measured 419.9 KB raw / 101.9 KB brotli with openvibe-shared 4.185.0 (Calm reduces, the solids and site marks, the You tab, the Dock out of the orb), 2026-11-10; 417: measured 416.4 KB raw / 100.9 KB brotli with openvibe-shared 4.179.0, 2026-11-09; 410: measured 409.2 KB raw / 99.4 KB brotli with openvibe-shared 4.173.0 (the wallet's cookie mode), 2026-11-09; 409: measured 408.1 KB raw / 99.0 KB brotli after merging main's Frame feed work (openvibe-shared 4.167.0), 2026-11-09; 406: measured 405.7 KB raw / 98.4 KB brotli with the shared page head's kit script on the home (openvibe-shared 4.163.0 + page head 1369721), 2026-11-09; 381.8: measured 375.8 KB raw / 91.1 KB brotli with openvibe-shared 4.159.0, plus room for 4.160.0–4.161.0 (the navbar's slots loader, the keyboard watcher, the pop motion), 2026-11-09; 373 with openvibe-shared 4.153.0 (2026-11-08: signed in to the network, signed in here, on phones too; a page can turn away a carried card); 372.3 with openvibe-shared 4.152.0 (2026-11-08: the menu marks the page you are on and opens without the keyboard on a phone, floating cards follow you across OpenVibe sites (overlay.js), and the navbar preconnects to the next OpenVibe site on intent); 369.6 with openvibe-shared 4.150.0 (2026-11-08: the bell opens the Dock on Activity, the Dock's phone header is in two rows, and the menu's orb is the close button with an open/close animation); 368.6 with openvibe-shared 4.149.0 (2026-11-08: the menu has tabs at the top; the phone's menu button is the orb of 4.148.0); 366.5 with openvibe-shared 4.118.0 (2026-11-06: the Locker re-auth link, the 44 px footer and TLD targets; main's 4.117.0 navbar item and the account-safety helper included); about 363.8 with both (2026-11-06: openvibe-shared 4.117.0's featured navbar item, +1.7 KB, and the account-safety helper, +8.5 KB; merged, to be confirmed by CI's measure); 353.6 with the account-safety helper on every page (2026-11-06: the one re-authentication path and the scheduled-deletion banner, +8.5 KB); 345.1 with openvibe-shared 4.114.0 (2026-11-06: the one Share sheet in the navbar (ov-share.js and ov-qr.js load on the first press); navbar.js gains its click handler); 343.9 with openvibe-shared 4.112.1 (2026-11-06: theme-loader.js (4.111.1) and the footer/appbar phone CSS (4.112.1)); 342.3 with openvibe-shared 4.109.2 (2026-11-03: OpenVibe's own select, the navbar loads ov-forms); 339.6 with openvibe-shared 4.107.0 (2026-11-03: open.<tld> sites are named for their domain; the brand knows its front door); 337.8 with openvibe-shared 4.103.0 (2026-11-02: the puzzle glyph, two-line sidebar rows, boost lets parsed pages go, eight solids) (2026-11-02); 334.1 openvibe-shared 4.70.0 (2026-11-01: the app bar, busy(), skeletons, view transitions); 333.2 with openvibe-shared 4.53.0: the footer's columns fold on phones (2026-10-30); 330.1 with openvibe-shared 4.52.0 (from 4.47.2): the seasonal fall, rich embeds, the Dock and its Chats tab (2026-10-30); 323.2 with openvibe-shared 4.47.2 (the comfort settings in theme-loader 4.45, touch targets 4.46, the grouped updates log 4.47.2); 313.7 with openvibe-shared 4.43.0 (room.css carries the kit tokens, 4.39.1; the kit's 4.39.0 rules); 306.4 openvibe-shared 4.25.0: the menu's orb beside the brand, both balances on phones, messages, the account sheet on phones and the bar stepping away on phones; 298.9 openvibe-shared 4.22.0: the navbar's magnifier search button and More dropdown (search.js and loading.js load only when used); 293.1 Shared 4.17.0: OpenCoins and Vibes chips (wallet: true) in the navbar; 279.1 openvibe-shared 4.15 footer: the compact footer opens into the whole one, and the pulse line; 269.3 with shared 4.11.0's seasonal navbar (Pumpkin Night and the season badge, +2.6); 249.8 with boost.js (13.1); 228.3