openvibers/openvibe.shared Commits
Shared 1.28.1: the account panel's sections keep their height (the accounts block no longer collapses into a sliver with its own scrollbar on phones) and its rows match the menu rows; the OpenVibe.Codes mark at rest shows its V (the looping draw animation was paused at its first, undrawn frame); phones no longer reserve 84-96 px of empty space under the footer (--ov-footer-clearance opts in). npm test 41/41.
Changes
@@@ -4,6 +4,13 @@ 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).++## 1.28.1 — 2026-09-28++++Fixes the owner reported from phones.++- **Account panel:** the panel scrolls as a whole and its sections keep their height. The panel is a flex column, which let its sections shrink, so on a height-limited phone screen the accounts block ("Switch to Anonymous", "Add another account") collapsed into a sliver with its own scrollbar. The account rows now have the menu rows' size, weight and icon width.++- **OpenVibe.Codes mark:** a mark at rest (every page after the first of a visit) paused the codes variant's looping V animation at its first frame, where the V is not drawn yet, so the logo showed only the ring and the dot. At rest the V is now fully drawn.++- **Footer:** phones no longer reserve 84–96 px of empty space under the footer. A page with a fixed bottom bar sets `--ov-footer-clearance` to its height; the default is none.++## 1.28.0 — 2026-09-28**`openvibe-shared/config` on PostgreSQL** (ADR-035: Media, Network and Live keep their configuration here). Given an openvibe-sdk/db handle, `createConfigStore` uses the new `config-pg.js`, which is the same store on the async handle. Revisions are immutable, activation is atomic, the last-known-good revision is restored when `onActivate` fails, secret values are redacted with a per-namespace fingerprint key, legacy values are imported, and so on. On this path `createConfigStore` returns a promise of the store: it has read, restored or seeded its active revision when the promise resolves. `get()` and `revision()` stay in memory. The other methods are async. The tables come from the service's migrations: `configSchema()` is their DDL, the SQLite schema converted. `adminRoutes` awaits either kind of store. The better-sqlite3 path is unchanged. `test/config-pg.test.js` runs every scenario of `test/config.test.js` on PGlite (openvibe-sdk v0.20.4, which returns `bytea` as a Buffer, is a dev dependency).@@@ -4,7 +4,7 @@"code": true,"wave": "W2","package": "openvibe-shared",-- "version": "1.28.0",++ "version": "1.28.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)",@@@ -364,8 +364,8 @@.ovf-compact-meta{color:var(--text-muted,#8b93ad);font-size:.74rem}@media (max-width:900px){.ovf-cols{grid-template-columns:repeat(2,minmax(0,1fr))}.ovf-col--brand{grid-column:1/-1}}@media (max-width:560px){-- .ovf{padding-bottom:calc(96px + env(safe-area-inset-bottom,0px))}-- .ovf[data-variant="compact"]{padding-bottom:calc(84px + env(safe-area-inset-bottom,0px))}++ .ovf{padding-bottom:calc(20px + var(--ov-footer-clearance,0px) + env(safe-area-inset-bottom,0px))}++ .ovf[data-variant="compact"]{padding-bottom:calc(14px + var(--ov-footer-clearance,0px) + env(safe-area-inset-bottom,0px))}.ovf-cols{gap:22px 16px}.ovf-bar{justify-content:center;text-align:center;padding-inline:0}.ovf-legal-inline{justify-content:center}@@@ -236,6 +236,10 @@animation: openvibe-slide-down .2s ease;}.openvibe-navbar-dropdown.open { display: flex; }++ /* The panel scrolls as a whole; its sections keep their height. A flex column lets children shrink by++ default, which squeezed the accounts block into a sliver with its own scrollbar when the panel was++ height-limited (phones). */++ .openvibe-navbar-dropdown > * { flex-shrink: 0; }/* Panels never leave the screen: dvh is the VISIBLE height on phones (100vh reaches behind the addressbar), the panel scrolls inside itself, and it is never wider than the viewport. */.openvibe-navbar-dropdown, .ovnav-launcher {@@@ -260,15 +264,15 @@max-height: 140px; overflow-y: auto;}.openvibe-navbar-dropdown-accounts .account-item {-- display: flex; align-items: center; gap: 8px; padding: 6px 8px;-- border-radius: 6px; cursor: pointer; font-size: 12px;-- color: var(--text-secondary, #b0b0b8); transition: background .12s;++ display: flex; align-items: center; gap: 8px; padding: 8px;++ border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 500;++ color: var(--text-primary, #e0e0e0); transition: background .12s;}.openvibe-navbar-dropdown-accounts .account-item:hover { background: var(--bg-hover, #2f2f3d); }.openvibe-navbar-dropdown-accounts .account-item img { width: 24px; height: 24px; border-radius: 50%; }.openvibe-navbar-dropdown-accounts .account-item.active { color: var(--accent-light, #60a5fa); font-weight: 600; }.openvibe-navbar-dropdown-accounts .add-account {-- display: flex; align-items: center; gap: 8px; padding: 6px 8px;++ display: flex; align-items: center; gap: 8px; padding: 8px;border-radius: 6px; cursor: pointer; font-size: 12px;color: var(--text-muted, #707080); transition: background .12s;text-decoration: none;@@@ -1405,11 +1409,11 @@</div>`).join('')}<div class="account-item" data-account-id="anon" style="${isAnon ? 'display:none' : ''}">-- <span style="width:24px;text-align:center">${navIcon('fa-user-secret')}</span>++ <span style="width:18px;text-align:center">${navIcon('fa-user-secret')}</span><span>Switch to Anonymous</span></div><a class="add-account" id="openvibe-add-account" href="${escapeAttr(addAccountHref)}">-- <span style="width:24px;text-align:center">${navIcon('fa-plus')}</span>++ <span style="width:18px;text-align:center">${navIcon('fa-plus')}</span><span>Add another account</span></a></div>@@@ -58,7 +58,8 @@.ov-mark:hover .c{animation-duration:.9s}.ov-mark[data-static] svg,.ov-mark[data-static] .g,.ov-mark[data-static] .c,.ov-mark[data-static] .v,.ov-mark[data-static] .d{animation:none!important}.ov-mark[data-static] .o{display:none}--.ov-mark[data-ov]:not([data-ovm-run]) svg,.ov-mark[data-ov]:not([data-ovm-run]) .g,.ov-mark[data-ov]:not([data-ovm-run]) .c,.ov-mark[data-ov]:not([data-ovm-run]) .d,.ov-mark[data-ov][data-variant=codes]:not([data-ovm-run]) .v{animation-play-state:paused!important}++.ov-mark[data-ov]:not([data-ovm-run]) svg,.ov-mark[data-ov]:not([data-ovm-run]) .g,.ov-mark[data-ov]:not([data-ovm-run]) .c,.ov-mark[data-ov]:not([data-ovm-run]) .d{animation-play-state:paused!important}++.ov-mark[data-ov][data-variant=codes]:not([data-ovm-run]) .v{animation:none!important;stroke-dashoffset:0}.ov-mark .p{fill:none;stroke:currentColor;stroke-width:4.4;stroke-linecap:round;transform:rotate(-90deg);transform-box:fill-box;transform-origin:center;stroke-dasharray:113.1;stroke-dashoffset:calc(113.1px * (1 - var(--ovm-p,0)));opacity:0;transition:stroke-dashoffset .35s cubic-bezier(.2,.8,.2,1),opacity .2s}.ov-mark[data-progress] .p{opacity:1}.ov-mark[data-progress] .c,.ov-mark[data-progress] .o{opacity:.25}@@@ -1,12 +1,12 @@{"name": "openvibe-shared",-- "version": "1.28.0",++ "version": "1.28.1","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-shared",-- "version": "1.28.0",++ "version": "1.28.1","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -1,6 +1,6 @@{"name": "openvibe-shared",-- "version": "1.28.0",++ "version": "1.28.1","description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site","main": "index.js","exports": {
@@ -4,6 +4,13 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
@@ -4,6 +4,13 @@ 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).
+## 1.28.1 — 2026-09-28
+
+Fixes the owner reported from phones.
+- **Account panel:** the panel scrolls as a whole and its sections keep their height. The panel is a flex column, which let its sections shrink, so on a height-limited phone screen the accounts block ("Switch to Anonymous", "Add another account") collapsed into a sliver with its own scrollbar. The account rows now have the menu rows' size, weight and icon width.
+- **OpenVibe.Codes mark:** a mark at rest (every page after the first of a visit) paused the codes variant's looping V animation at its first frame, where the V is not drawn yet, so the logo showed only the ring and the dot. At rest the V is now fully drawn.
+- **Footer:** phones no longer reserve 84–96 px of empty space under the footer. A page with a fixed bottom bar sets `--ov-footer-clearance` to its height; the default is none.
+
## 1.28.0 — 2026-09-28
## 1.28.0 — 2026-09-28
**`openvibe-shared/config` on PostgreSQL** (ADR-035: Media, Network and Live keep their configuration here). Given an openvibe-sdk/db handle, `createConfigStore` uses the new `config-pg.js`, which is the same store on the async handle. Revisions are immutable, activation is atomic, the last-known-good revision is restored when `onActivate` fails, secret values are redacted with a per-namespace fingerprint key, legacy values are imported, and so on. On this path `createConfigStore` returns a promise of the store: it has read, restored or seeded its active revision when the promise resolves. `get()` and `revision()` stay in memory. The other methods are async. The tables come from the service's migrations: `configSchema()` is their DDL, the SQLite schema converted. `adminRoutes` awaits either kind of store. The better-sqlite3 path is unchanged. `test/config-pg.test.js` runs every scenario of `test/config.test.js` on PGlite (openvibe-sdk v0.20.4, which returns `bytea` as a Buffer, is a dev dependency).
**`openvibe-shared/config` on PostgreSQL** (ADR-035: Media, Network and Live keep their configuration here). Given an openvibe-sdk/db handle, `createConfigStore` uses the new `config-pg.js`, which is the same store on the async handle. Revisions are immutable, activation is atomic, the last-known-good revision is restored when `onActivate` fails, secret values are redacted with a per-namespace fingerprint key, legacy values are imported, and so on. On this path `createConfigStore` returns a promise of the store: it has read, restored or seeded its active revision when the promise resolves. `get()` and `revision()` stay in memory. The other methods are async. The tables come from the service's migrations: `configSchema()` is their DDL, the SQLite schema converted. `adminRoutes` awaits either kind of store. The better-sqlite3 path is unchanged. `test/config-pg.test.js` runs every scenario of `test/config.test.js` on PGlite (openvibe-sdk v0.20.4, which returns `bytea` as a Buffer, is a dev dependency).
@@ -4,7 +4,7 @@
@@ -4,7 +4,7 @@
"code": true,
"code": true,
"wave": "W2",
"wave": "W2",
"package": "openvibe-shared",
"package": "openvibe-shared",
- "version": "1.28.0",
+ "version": "1.28.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)",
@@ -364,8 +364,8 @@
@@ -364,8 +364,8 @@
.ovf-compact-meta{color:var(--text-muted,#8b93ad);font-size:.74rem}
.ovf-compact-meta{color:var(--text-muted,#8b93ad);font-size:.74rem}
@media (max-width:900px){.ovf-cols{grid-template-columns:repeat(2,minmax(0,1fr))}.ovf-col--brand{grid-column:1/-1}}
@media (max-width:900px){.ovf-cols{grid-template-columns:repeat(2,minmax(0,1fr))}.ovf-col--brand{grid-column:1/-1}}
@media (max-width:560px){
@media (max-width:560px){
- .ovf{padding-bottom:calc(96px + env(safe-area-inset-bottom,0px))}
- .ovf[data-variant="compact"]{padding-bottom:calc(84px + env(safe-area-inset-bottom,0px))}
+ .ovf{padding-bottom:calc(20px + var(--ov-footer-clearance,0px) + env(safe-area-inset-bottom,0px))}
+ .ovf[data-variant="compact"]{padding-bottom:calc(14px + var(--ov-footer-clearance,0px) + env(safe-area-inset-bottom,0px))}
.ovf-cols{gap:22px 16px}
.ovf-cols{gap:22px 16px}
.ovf-bar{justify-content:center;text-align:center;padding-inline:0}
.ovf-bar{justify-content:center;text-align:center;padding-inline:0}
.ovf-legal-inline{justify-content:center}
.ovf-legal-inline{justify-content:center}
@@ -236,6 +236,10 @@
@@ -236,6 +236,10 @@
animation: openvibe-slide-down .2s ease;
animation: openvibe-slide-down .2s ease;
}
}
.openvibe-navbar-dropdown.open { display: flex; }
.openvibe-navbar-dropdown.open { display: flex; }
+ /* The panel scrolls as a whole; its sections keep their height. A flex column lets children shrink by
+ default, which squeezed the accounts block into a sliver with its own scrollbar when the panel was
+ height-limited (phones). */
+ .openvibe-navbar-dropdown > * { flex-shrink: 0; }
/* Panels never leave the screen: dvh is the VISIBLE height on phones (100vh reaches behind the address
/* Panels never leave the screen: dvh is the VISIBLE height on phones (100vh reaches behind the address
bar), the panel scrolls inside itself, and it is never wider than the viewport. */
bar), the panel scrolls inside itself, and it is never wider than the viewport. */
.openvibe-navbar-dropdown, .ovnav-launcher {
.openvibe-navbar-dropdown, .ovnav-launcher {
@@ -260,15 +264,15 @@
@@ -260,15 +264,15 @@
max-height: 140px; overflow-y: auto;
max-height: 140px; overflow-y: auto;
}
}
.openvibe-navbar-dropdown-accounts .account-item {
.openvibe-navbar-dropdown-accounts .account-item {
- display: flex; align-items: center; gap: 8px; padding: 6px 8px;
- border-radius: 6px; cursor: pointer; font-size: 12px;
- color: var(--text-secondary, #b0b0b8); transition: background .12s;
+ display: flex; align-items: center; gap: 8px; padding: 8px;
+ border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 500;
+ color: var(--text-primary, #e0e0e0); transition: background .12s;
}
}
.openvibe-navbar-dropdown-accounts .account-item:hover { background: var(--bg-hover, #2f2f3d); }
.openvibe-navbar-dropdown-accounts .account-item:hover { background: var(--bg-hover, #2f2f3d); }
.openvibe-navbar-dropdown-accounts .account-item img { width: 24px; height: 24px; border-radius: 50%; }
.openvibe-navbar-dropdown-accounts .account-item img { width: 24px; height: 24px; border-radius: 50%; }
.openvibe-navbar-dropdown-accounts .account-item.active { color: var(--accent-light, #60a5fa); font-weight: 600; }
.openvibe-navbar-dropdown-accounts .account-item.active { color: var(--accent-light, #60a5fa); font-weight: 600; }
.openvibe-navbar-dropdown-accounts .add-account {
.openvibe-navbar-dropdown-accounts .add-account {
- display: flex; align-items: center; gap: 8px; padding: 6px 8px;
+ display: flex; align-items: center; gap: 8px; padding: 8px;
border-radius: 6px; cursor: pointer; font-size: 12px;
border-radius: 6px; cursor: pointer; font-size: 12px;
color: var(--text-muted, #707080); transition: background .12s;
color: var(--text-muted, #707080); transition: background .12s;
text-decoration: none;
text-decoration: none;
@@ -1405,11 +1409,11 @@
@@ -1405,11 +1409,11 @@
</div>
</div>
`).join('')}
`).join('')}
<div class="account-item" data-account-id="anon" style="${isAnon ? 'display:none' : ''}">
<div class="account-item" data-account-id="anon" style="${isAnon ? 'display:none' : ''}">
- <span style="width:24px;text-align:center">${navIcon('fa-user-secret')}</span>
+ <span style="width:18px;text-align:center">${navIcon('fa-user-secret')}</span>
<span>Switch to Anonymous</span>
<span>Switch to Anonymous</span>
</div>
</div>
<a class="add-account" id="openvibe-add-account" href="${escapeAttr(addAccountHref)}">
<a class="add-account" id="openvibe-add-account" href="${escapeAttr(addAccountHref)}">
- <span style="width:24px;text-align:center">${navIcon('fa-plus')}</span>
+ <span style="width:18px;text-align:center">${navIcon('fa-plus')}</span>
<span>Add another account</span>
<span>Add another account</span>
</a>
</a>
</div>
</div>
@@ -58,7 +58,8 @@
@@ -58,7 +58,8 @@
.ov-mark:hover .c{animation-duration:.9s}
.ov-mark:hover .c{animation-duration:.9s}
.ov-mark[data-static] svg,.ov-mark[data-static] .g,.ov-mark[data-static] .c,.ov-mark[data-static] .v,.ov-mark[data-static] .d{animation:none!important}
.ov-mark[data-static] svg,.ov-mark[data-static] .g,.ov-mark[data-static] .c,.ov-mark[data-static] .v,.ov-mark[data-static] .d{animation:none!important}
.ov-mark[data-static] .o{display:none}
.ov-mark[data-static] .o{display:none}
-.ov-mark[data-ov]:not([data-ovm-run]) svg,.ov-mark[data-ov]:not([data-ovm-run]) .g,.ov-mark[data-ov]:not([data-ovm-run]) .c,.ov-mark[data-ov]:not([data-ovm-run]) .d,.ov-mark[data-ov][data-variant=codes]:not([data-ovm-run]) .v{animation-play-state:paused!important}
+.ov-mark[data-ov]:not([data-ovm-run]) svg,.ov-mark[data-ov]:not([data-ovm-run]) .g,.ov-mark[data-ov]:not([data-ovm-run]) .c,.ov-mark[data-ov]:not([data-ovm-run]) .d{animation-play-state:paused!important}
+.ov-mark[data-ov][data-variant=codes]:not([data-ovm-run]) .v{animation:none!important;stroke-dashoffset:0}
.ov-mark .p{fill:none;stroke:currentColor;stroke-width:4.4;stroke-linecap:round;transform:rotate(-90deg);transform-box:fill-box;transform-origin:center;stroke-dasharray:113.1;stroke-dashoffset:calc(113.1px * (1 - var(--ovm-p,0)));opacity:0;transition:stroke-dashoffset .35s cubic-bezier(.2,.8,.2,1),opacity .2s}
.ov-mark .p{fill:none;stroke:currentColor;stroke-width:4.4;stroke-linecap:round;transform:rotate(-90deg);transform-box:fill-box;transform-origin:center;stroke-dasharray:113.1;stroke-dashoffset:calc(113.1px * (1 - var(--ovm-p,0)));opacity:0;transition:stroke-dashoffset .35s cubic-bezier(.2,.8,.2,1),opacity .2s}
.ov-mark[data-progress] .p{opacity:1}
.ov-mark[data-progress] .p{opacity:1}
.ov-mark[data-progress] .c,.ov-mark[data-progress] .o{opacity:.25}
.ov-mark[data-progress] .c,.ov-mark[data-progress] .o{opacity:.25}
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "1.28.0",
+ "version": "1.28.1",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "1.28.0",
+ "version": "1.28.1",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "1.28.0",
+ "version": "1.28.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",
"exports": {
"exports": {