openvibers/openvibe.shared Commits

4.106.3: a site on its own open.<tld> domain is named for it (#273)

OP OpenVibers committed 10 days ago · 5a3fe1eb5a · 11 files, +62 −15

open.shoes read "OpenVibe.Shoes" in the bar and its name linked to openvibe.shoes (the owner's screenshot, 11-02).
The navbar reads <sub>.open.<tld> hosts as Open.<Tld>, a config may set brand.core itself, and the brand carries
its front door as apex, which the name's links use. The footer's fallback and the installed app's name say Open.Shoes.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex a87f206..76b4edd 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -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.
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
++## 4.106.3 — 2026-11-02
++
++- **A site on its own open.<tld> domain is named for it.** open.shoes read "OpenVibe.Shoes" in the bar and its name
++ linked to openvibe.shoes (the owner's screenshot). The navbar now reads `<sub>.open.<tld>` hosts as Open.<Tld>, and a
++ config can say `brand: { core: 'Open' }` itself off the domain (localhost, previews). The brand carries its front door
++ as `apex` (open.shoes, openre.stream, openvibe.<tld>), and the name's links go there. The footer's fallback and the
++ installed app's name (`icons/sites.json`, from the manifest's first domain) say Open.Shoes too.
++
## 4.106.2 — 2026-11-02
- **The site's name reads whole on a signed-in phone.** On openvibe.live a phone showed "Li…" beside the balances, the
ddiff --git a/README.md b/README.md
iindex b71cbf6..7128f7a 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.106.2 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
++**Status:** alpha, v4.106.3 (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 e525e36..f5e1843 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -4,7 +4,7 @@
"code": true,
"wave": "W2",
"package": "openvibe-shared",
-- "version": "4.106.2",
++ "version": "4.106.3",
"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/footer.js b/footer.js
iindex 5914bac..79d055e 100644
---- a/footer.js
++++ b/footer.js
@@@ -188,6 +188,7 @@
let m = h.match(/^(?:(.+)\.)?openvibe\.([a-z]+)$/);
if (m) { const sub = m[1] && m[1] !== 'www' ? m[1] : null; return [sub ? (sub.length <= 4 ? sub.toUpperCase() : cap(sub)) : null, 'OpenVibe', TLD_LABELS[m[2]] || cap(m[2])].filter(Boolean).join('.'); }
if ((m = h.match(/^(?:(.+)\.)?openre\.stream$/))) return 'OpenRestream';
++ if ((m = h.match(/^(?:(.+)\.)?open\.([a-z]+)$/))) return `Open.${TLD_LABELS[m[2]] || cap(m[2])}`;
const found = NETWORK.find(n => n.id === service);
if (service && TLD_LABELS[service]) return `OpenVibe.${TLD_LABELS[service]}`;
if (found && found.name !== 'Account & themes' && found.name !== 'The whole network →') return `${cap(service)}.OpenVibe.Tools`;
ddiff --git a/icons/sites.json b/icons/sites.json
iindex 327e3d1..35a6aff 100644
---- a/icons/sites.json
++++ b/icons/sites.json
@@@ -200,7 +200,7 @@
"icon": "network"
},
"shoes": {
-- "name": "OpenVibe.Shoes",
++ "name": "Open.Shoes",
"description": "Every shoe, every market, the right price",
"icon": "network"
},
ddiff --git a/navbar.js b/navbar.js
iindex 1577794..a6818c0 100644
---- a/navbar.js
++++ b/navbar.js
@@@ -675,7 +675,7 @@
}
// ─── Brand from hostname ───────────────────────────────────
-- // Every property is <sub?>.openvibe.<tld> (plus openre.stream). The navbar spells the
++ // Every property is <sub?>.openvibe.<tld> (plus openre.stream and open.<tld>). The navbar spells the
// whole name — Pastes.OpenVibe.Tools, not "Paste.OpenVibe" — because the subdomain and
// the TLD are what tell a visitor where they are in the network.
const TLD_LABELS = {
@@@ -728,6 +728,8 @@
let m = host.match(/^(?:(.+)\.)?openvibe\.([a-z]+)$/);
if (m) { sub = m[1] && m[1] !== 'www' ? m[1] : null; tld = m[2]; }
else if ((m = host.match(/^(?:(.+)\.)?openre\.stream$/))) { sub = m[1] && m[1] !== 'www' ? m[1] : null; core = 'OpenRe'; tld = 'stream'; joined = true; }
++ // A site on its own open.<tld> domain (open.shoes) is named for it: Open.Shoes, not OpenVibe.Shoes.
++ else if ((m = host.match(/^(?:(.+)\.)?open\.([a-z]+)$/))) { sub = m[1] && m[1] !== 'www' ? m[1] : null; core = 'Open'; tld = m[2]; }
// Off-network hosts (localhost, previews): fall back to the service id.
if (!tld) { tld = SERVICE_TLD[_config.service] || (SERVICE_SUB[_config.service] ? 'tools' : 'network'); sub = SERVICE_SUB[_config.service] || null; }
// Legacy brandName ("Paste.OpenVibe", "OpenVibe.Live") still steers the segments.
@@@ -739,18 +741,21 @@
}
if (b.sub !== undefined) sub = b.sub ? String(b.sub).toLowerCase() : null;
if (b.tld) tld = String(b.tld).toLowerCase();
++ if (b.core && !joined) core = String(b.core);
const subText = b.subLabel || (sub ? subLabel(sub) : null);
const tldText = b.tldLabel || (joined ? tld : TLD_LABELS[tld] || titleCase(tld));
const site = joined ? `${core}${tldText}` : `${core}.${tldText}`;
const name = b.name || [subText, site].filter(Boolean).join('.');
const icon = b.icon || _config.brandIcon || null;
const variant = b.variant || (joined ? 'stream' : tld);
-- return { sub, subText, core, tld, tldText, joined, site, name, short: subText || site, icon, variant, tag: b.tag || null, href: b.href || '/' };
++ // The site's own front door: openre.stream, open.shoes, openvibe.<tld>.
++ const apex = b.apex || (joined ? 'https://openre.stream/' : `https://${core.toLowerCase()}.${tld}/`);
++ return { sub, subText, core, tld, tldText, joined, site, name, short: subText || site, icon, variant, tag: b.tag || null, href: b.href || '/', apex };
}
/** Where each brand segment goes: sub → this tool, OpenVibe → the network, TLD → the site's apex. */
function brandLinks(brand) {
-- const apex = brand.joined ? 'https://openre.stream/' : `https://openvibe.${brand.tld}/`;
++ const apex = brand.apex || (brand.joined ? 'https://openre.stream/' : `https://openvibe.${brand.tld}/`);
if (!brand.subText) return { sub: brand.href, core: brand.href, tld: brand.href, apex };
return { sub: brand.href, core: brand.joined ? apex : 'https://openvibe.network/', tld: apex, apex };
}
@@@ -769,7 +774,7 @@
const dot = '<span class="b-dot">.</span>';
const join = brand.joined ? '' : dot;
const text = brand.subText
-- ? seg('b-sub', brand.subText, to.sub, brand.name) + dot + seg('b-core', brand.core, to.core, brand.joined ? brand.site : 'OpenVibe Network') + join + seg('b-tld', brand.tldText, to.tld, `All of ${brand.site}`)
++ ? seg('b-sub', brand.subText, to.sub, brand.name) + dot + seg('b-core', brand.core, to.core, brand.core === 'OpenVibe' ? 'OpenVibe Network' : brand.site) + join + seg('b-tld', brand.tldText, to.tld, `All of ${brand.site}`)
: seg('b-core', brand.core, to.core, brand.name) + join + seg('b-tld', brand.tldText, to.tld, brand.name);
const mark = brand.icon
? `<i class="fa-solid ${escapeAttr(brand.icon)}"></i>`
ddiff --git a/package-lock.json b/package-lock.json
iindex e80b95f..b9b0735 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-shared",
-- "version": "4.106.2",
++ "version": "4.106.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-shared",
-- "version": "4.106.2",
++ "version": "4.106.3",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0",
ddiff --git a/package.json b/package.json
iindex 5b87871..de61b46 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "4.106.2",
++ "version": "4.106.3",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/scripts/build-icons.js b/scripts/build-icons.js
iindex 21e8959..3250cc0 100644
---- a/scripts/build-icons.js
++++ b/scripts/build-icons.js
@@@ -33,12 +33,14 @@ function siteOf(id) {
if (d) return { id, name: d.name, tagline: d.tagline };
const m = JSON.parse(fs.readFileSync(path.join(SERVICES, `${id}.json`), 'utf8'));
if (!m.site) throw new Error(`${id}: no site block`);
-- return { id, name: m.site.name, tagline: m.site.tagline || '' };
++ return { id, name: m.site.name, tagline: m.site.tagline || '', domains: m.domains || [] };
}
--/** The product's full name: "OpenVibe." + the site name, unless it already carries its own ("OpenRestream"). */
--function productName(name) {
-- return /^Open(Re)(.+)$/.test(name) ? name : `OpenVibe.${name}`;
++/** The product's full name: "OpenVibe." + the site name, unless it already carries its own ("OpenRestream") or lives on
++ * its own open.<tld> domain (open.shoes → "Open.Shoes"). */
++function productName(name, domains = []) {
++ if (/^Open(Re)(.+)$/.test(name)) return name;
++ return /^open\.[a-z]+$/.test(domains[0] || '') ? `Open.${name}` : `OpenVibe.${name}`;
}
(async () => {
@@@ -49,7 +51,7 @@ function productName(name) {
for (const id of serviceIds()) {
const site = siteOf(id);
const variant = social.iconSite(id);
-- sites[id] = { name: productName(site.name), description: site.tagline, icon: variant };
++ sites[id] = { name: productName(site.name, site.domains), description: site.tagline, icon: variant };
variants.add(variant);
}
for (const variant of [...variants].sort()) {
ddiff --git a/test/app-icons.test.js b/test/app-icons.test.js
iindex e58bf80..2c62f46 100644
---- a/test/app-icons.test.js
++++ b/test/app-icons.test.js
@@@ -102,6 +102,10 @@ function pngSize(buf) {
const openre = await (await fetch(base + serve.manifestUrl('openre'))).json();
assert.strictEqual(openre.name, 'OpenRestream');
assert.strictEqual(openre.short_name, 'OpenRestream');
++ // A site on its own open.<tld> domain installs under that name (11-02: open.shoes was "OpenVibe.Shoes").
++ const shoes = await (await fetch(base + serve.manifestUrl('shoes'))).json();
++ assert.strictEqual(shoes.name, 'Open.Shoes');
++ assert.strictEqual(shoes.short_name, 'Open.Shoes');
assert.strictEqual((await fetch(base + '/shared/manifest/nope.webmanifest')).status, 404);
assert.strictEqual((await fetch(base + '/shared/manifest/wiki.png')).status, 404);
ddiff --git a/test/navbar-brand.test.js b/test/navbar-brand.test.js
iindex 232ee5d..facde84 100644
---- a/test/navbar-brand.test.js
++++ b/test/navbar-brand.test.js
@@@ -108,6 +108,33 @@ function load(hostname, opts = {}) {
navbar.init({ service: 'network' });
assert.strictEqual(navbar.brand().name, 'OpenVibe.VIP');
}
++{
++ // A site on its own open.<tld> domain is named for it (11-02: open.shoes read "OpenVibe.Shoes"), and its
++ // segments lead to open.shoes, not openvibe.shoes. Shoes' own config (tld + label) keeps that.
++ const { navbar } = load('open.shoes');
++ navbar.init({ service: 'shoes', brand: { tld: 'shoes', tldLabel: 'Shoes' } });
++ const b = navbar.brand();
++ assert.strictEqual(b.name, 'Open.Shoes');
++ assert.strictEqual(b.core, 'Open');
++ assert.strictEqual(b.apex, 'https://open.shoes/');
++ assert.strictEqual(b.variant, 'shoes');
++}
++{
++ // Off the domain (localhost, previews) the config can say so itself.
++ const { navbar } = load('localhost');
++ navbar.init({ service: 'shoes', brand: { core: 'Open', tld: 'shoes', tldLabel: 'Shoes' } });
++ assert.strictEqual(navbar.brand().name, 'Open.Shoes');
++ assert.strictEqual(navbar.brand().apex, 'https://open.shoes/');
++}
++{
++ // openvibe.<tld> and openre.stream keep their front doors.
++ let { navbar } = load('openvibe.live');
++ navbar.init({ service: 'live' });
++ assert.strictEqual(navbar.brand().apex, 'https://openvibe.live/');
++ ({ navbar } = load('openre.stream'));
++ navbar.init({ service: 'openre' });
++ assert.strictEqual(navbar.brand().apex, 'https://openre.stream/');
++}
// ── off-network hosts fall back to the service id ────────────
{
const { navbar } = load('localhost');