openvibers/openvibe.live Commits

openvibe-shared v4.95.0; on a phone, Recent Pastes is a rail that loads as you swipe (#553)

OP OpenVibers committed 11 days ago · fcaa64fcae · 9 files, +44 −22

* openvibe-shared v4.95.0 (was v4.94.0)

Opened by scripts/pin-bump.js in OpenVibers/OpenVibe.Shared after the v4.95.0 release.

* Phone home: Recent Pastes is a rail that loads as you swipe; a section you flung past is shown

* Budgets for 4.95.0 with the Pastes rail; the catch-up test names the Pastes rail

Changes

ddiff --git a/README.md b/README.md
iindex 72fc8a90..dcbfb52f 100644
---- a/README.md
++++ b/README.md
@@@ -177,5 +177,5 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL
<!-- versions:start -->
- openvibe-contracts: v0.222.0
- openvibe-sdk: v0.81.0
--- openvibe-shared: v4.94.0
++- openvibe-shared: v4.95.0
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 78469bd9..fdd0fe47 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -7,7 +7,7 @@
"domain": "openvibe.live (www, ingest, whip)",
"contracts": "openvibe-contracts v0.222.0",
"sdk": "openvibe-sdk v0.81.0",
-- "shared": "openvibe-shared v4.94.0",
++ "shared": "openvibe-shared v4.95.0",
"capabilities": [
"live.chat_context.read",
"live.chat_effects.write",
ddiff --git a/package-lock.json b/package-lock.json
iindex 577f2970..d08959e6 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -25,7 +25,7 @@
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.81.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.94.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.95.0",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sdp-transform": "^3.0.0",
@@@ -2884,9 +2884,9 @@
}
},
"node_modules/openvibe-shared": {
-- "version": "4.94.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.94.0",
-- "integrity": "sha512-iMsgL7a5vxouygXsWxQACJrnRubhpe6tnMAKjiJZ0m6Fn+hFcJjQzrI/fERZB3ToC+xK4zO6b6hsm4zK6x4MYg==",
++ "version": "4.95.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.95.0",
++ "integrity": "sha512-jY0ZKFQMvx3NiyKxHkZ950nq7P8CtlzlP4OOnTkJf0nyVVG93qT4JE0udr8bF8Kwm7FrzkLgfaLB1qDrjH22eg==",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0",
ddiff --git a/package.json b/package.json
iindex aa89871f..b173438b 100644
---- a/package.json
++++ b/package.json
@@@ -44,7 +44,7 @@
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.81.0",
-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.94.0",
++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.95.0",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sdp-transform": "^3.0.0",
ddiff --git a/public/css/features/home.css b/public/css/features/home.css
iindex 29565396..fd8bc2cf 100644
---- a/public/css/features/home.css
++++ b/public/css/features/home.css
@@@ -1332,22 +1332,23 @@ body.egg-rainbow { animation: eggRainbow 2.6s linear; }
.home-tabbox [data-panel="network"] .network-hub-header h3 { display: none; }
/* Catch up on a phone: each rail is one row of cards that scrolls sideways, so all five fit in a few screens
-- and nothing is behind a tab. */
-- #home-catchup .stream-grid {
++ and nothing is behind a tab. Recent Pastes rides the same rail (2026-11-02): it was two cards a row with a
++ "Load 2 more · 309 left" button under them. */
++ #home-catchup .stream-grid, #home-catchup .home-pastes-list {
display: grid; grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: minmax(232px, 74%); align-items: start;
gap: 12px; margin-inline: -16px; padding: 2px 16px 10px; scroll-padding-inline: 16px;
overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none;
}
-- #home-catchup .stream-grid::-webkit-scrollbar { display: none; }
++ #home-catchup .stream-grid::-webkit-scrollbar, #home-catchup .home-pastes-list::-webkit-scrollbar { display: none; }
/* ov-density.js reads a sideways rail's implicit columns as "too many" and clamps the grid to repeat(N, minmax(0, 1fr))
with !important, which squeezed the first cards to a few pixels (production's Recently Online showed an 81 px
sliver, 2026-11-01). A rail has no columns to choose: its own track sizing wins. */
-- #home-catchup .stream-grid[data-ovd-cols] { grid-template-columns: none !important; }
-- #home-catchup .stream-grid > * { scroll-snap-align: start; min-width: 0; }
-- #home-catchup .stream-grid > .empty-state { grid-column: auto; min-width: calc(100vw - 32px); }
++ #home-catchup .stream-grid[data-ovd-cols], #home-catchup .home-pastes-list[data-ovd-cols] { grid-template-columns: none !important; }
++ #home-catchup .stream-grid > *, #home-catchup .home-pastes-list > * { scroll-snap-align: start; min-width: 0; }
++ #home-catchup .stream-grid > .empty-state, #home-catchup .home-pastes-list > .empty-state { grid-column: auto; min-width: calc(100vw - 32px); }
/* The end of a sideways rail (2026-11-01): placeholder cards where the next ones load by themselves as the reader
swipes toward them (app-home.js homeRailTail), then one "View all" card once every card is in. */
-- #home-catchup .stream-grid > .home-rail-tail { display: flex; }
++ #home-catchup .stream-grid > .home-rail-tail, #home-catchup .home-pastes-list > .home-rail-tail { display: flex; }
.home-rail-skel {
flex-direction: column; gap: 10px; padding-bottom: 14px; overflow: hidden;
border-radius: 14px; border: 1px solid var(--border); background: var(--bg-card);
ddiff --git a/public/js/home-fx.js b/public/js/home-fx.js
iindex 7a20dc16..79673bdf 100644
---- a/public/js/home-fx.js
++++ b/public/js/home-fx.js
@@@ -111,12 +111,25 @@
const sweep = () => {
document.querySelectorAll('.hfx-reveal:not(.is-in)').forEach(el => {
const r = el.getBoundingClientRect();
-- if (r.height && r.top < window.innerHeight && r.bottom > 0) el.classList.add('is-in');
++ if (r.height && r.top < window.innerHeight) el.classList.add('is-in'); // on screen, or already passed
});
revealPasses++;
revealSafety = revealPasses < 5 ? setTimeout(sweep, 2000) : 0;
};
revealSafety = setTimeout(sweep, 1200);
++ // A fling can carry a section past the screen between two frames, so the observer never reports it, and the
++ // sweeps above stop after ten seconds. When scrolling stops, anything above the bottom of the screen that is
++ // still waiting is shown: one query per pause, never during the scroll.
++ let settle = 0;
++ window.addEventListener('scroll', () => {
++ clearTimeout(settle);
++ settle = setTimeout(() => {
++ document.querySelectorAll('.hfx-reveal:not(.is-in)').forEach(el => {
++ const r = el.getBoundingClientRect();
++ if (r.height && r.top < window.innerHeight) el.classList.add('is-in');
++ });
++ }, 140);
++ }, { passive: true });
}
const REVEAL_SEL = '#page-home .section-header, #page-home .stream-card, #page-home .home-cta-banner, #page-home .home-star-section, #page-home .rs-hero-mount, #page-home .pulse-grid > *, #page-home .moments-row > *, #page-home .home-digest';
@@@ -125,7 +138,9 @@
let lateIo = null;
function attachReveal(roots) {
if (!('IntersectionObserver' in window)) return;
-- if (!io) io = new IntersectionObserver((entries) => { for (const en of entries) if (en.isIntersecting) { en.target.classList.add('is-in'); io.unobserve(en.target); } }, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });
++ // Seen, or scrolled past before the observer caught it (a fast swipe): either way it shows. A section passed
++ // over stayed at opacity 0 until the reader came back to it, a blank band on the way down (2026-11-02).
++ if (!io) io = new IntersectionObserver((entries) => { for (const en of entries) if (en.isIntersecting || en.boundingClientRect.bottom < 0) { en.target.classList.add('is-in'); io.unobserve(en.target); } }, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 });
if (!lateIo) lateIo = new IntersectionObserver((entries) => {
for (const en of entries) {
const el = en.target; lateIo.unobserve(el);
ddiff --git a/scripts/perf/check-budgets.js b/scripts/perf/check-budgets.js
iindex a1250c49..9b1840cf 100644
---- a/scripts/perf/check-budgets.js
++++ b/scripts/perf/check-budgets.js
@@@ -22,8 +22,8 @@ const BUDGETS = {
htmlRawKB: 128, // 119.3 with the icons drawn on the server (2026-11-01, plan L54 K4: the OpenVibe glyphs in the HTML, 20.8 KB brotli); measured 94.9 before
htmlBrotliKB: 30,
jsFiles: 26, // measured 23 (incl. route scripts for "/")
-- jsRawKB: 1263, // 1262.1 (2026-11-02: openvibe-shared 4.94.0, the Audio chip, the go-live card's labels); 1259.8 (2026-11-02: content labels on cards and the player, L1); 1257.5 (2026-11-01: the Catch up rails load by themselves as you swipe, no Load more on a phone); 1254.2 (2026-11-01: openvibe-shared 4.87.0), about 1255 with #534 (feed and card avatars at their size); 1253.8 (2026-11-01: avatars ask Media for their drawn width, ovImgW); 1252.3 (2026-11-01: Following in the topics row, the picked topic remembered); 1250.2 (2026-11-01: topics live now, readable chat names on light themes); 1247.7 openvibe-shared 4.70.0 (2026-11-01: the app bar, busy(), skeletons, view transitions); 1244.3 with openvibe-shared 4.66.0: the mark with depth and motion, ov-fx loaded on request (2026-10-31); 1243.5 with openvibe-shared 4.64.0: the look travels between sites, the Look panel in the menu (2026-10-31); 1242.7 with openvibe-shared 4.59.0: the Pair mark beside the name, set in the brand face, and the ring on page moves (2026-10-31); 1238.8 with the home's featured stream once and the intro closed on phones (2026-10-31); 1237.4 with the home's two more tab blocks on phones (2026-10-30); 1233.9 with the home's Catch up tabs on phones (2026-10-30); 1230.8 with openvibe-shared 4.53.0: the footer's columns fold on phones (2026-10-30); 1227.8 with openvibe-shared 4.52.0: the Dock's loader and its Chats tab (2026-10-30); 1223.5 with openvibe-shared 4.48.0 (the site switcher on the mark, the menu's Start tab and quick tools; Kit's touch hit areas and the phone navbar fix, 4.44-4.47.3); 1215.4 with openvibe-shared 4.43.0 (room.css carries the kit tokens, 4.39.1; the kit's 4.39.0 rules); measured 1203.9 (2026-10-28: openvibe-shared 4.30.0, the bell center: tabs with counts, the Unread filter, thumbnails, swipe, the phone sheet; 4.31.0's ✦ adds about 3 KB); 1187.2 (2026-10-28: openvibe-shared 4.27.0, Notifications 2.0: opaque themed toasts held while a menu is open, grouping, priority, System cards and the rich bell panel); 1158.8 (2026-10-28: openvibe-shared 4.25.0, the menu's orb, messages, the phone sheet and auto-hide in the navbar; the messenger opens from the bar)
-- jsBrotliKB: 295.5, // 295.1 (2026-11-02: openvibe-shared 4.94.0, the Audio chip, the go-live card's labels); 294.6 (2026-11-02: content labels on cards and the player, L1); 294.1 (2026-11-01: the Catch up rails load by themselves as you swipe); 292.6 openvibe-shared 4.70.0 (2026-11-01: the app bar, busy(), skeletons, view transitions); 291.1 with openvibe-shared 4.57.1 (the Dock's async token and scope-aware controls, 4.57.0; the tool frame's facts, 4.56.0) (2026-10-31); 290.6 with the home's two more tab blocks on phones (2026-10-30); 289.8 with the home's Catch up tabs on phones (2026-10-30); 288.2 with openvibe-shared 4.52.0: the Dock's loader and its Chats tab (2026-10-30); 287.4 with openvibe-shared 4.48.0 (the site switcher on the mark, the menu's Start tab and quick tools; Kit's touch hit areas and the phone navbar fix, 4.44-4.47.3); measured 283.2 (2026-10-28: openvibe-shared 4.30.0, the bell center as above); 279.6 (2026-10-28: openvibe-shared 4.27.0, Notifications 2.0 as above); 273.1 (2026-10-28: openvibe-shared 4.25.0 as above); 270.5 (2026-10-28: openvibe-shared 4.22.0, the navbar's search magnifier with / and Ctrl+K and the More menu; search.js and loading.js load only when used); 269.6 (2026-10-28: openvibe-shared 4.19.0, the account menu's gear and people-group icons inline; 268 on 2026-10-27: openvibe-shared 4.16.0, the folded balance chip, Across OpenVibe with site marks and the icon preload in the navbar); 265.3 (2026-10-27: openvibe-shared 4.15.0, the compact footer opens into the whole one, and the pulse line); 263.3 on 10-23 (openvibe-shared 4.9.0, status text twins in every theme); 262.5 on 10-17 (openvibe-shared 4.4.0, every site in the launcher)
++ jsRawKB: 1267, // 1266.3 (2026-11-02: openvibe-shared 4.95.0, keyboard-reachable scrollers; the phone Pastes rail); 1262.1 (2026-11-02: openvibe-shared 4.94.0, the Audio chip, the go-live card's labels); 1259.8 (2026-11-02: content labels on cards and the player, L1); 1257.5 (2026-11-01: the Catch up rails load by themselves as you swipe, no Load more on a phone); 1254.2 (2026-11-01: openvibe-shared 4.87.0), about 1255 with #534 (feed and card avatars at their size); 1253.8 (2026-11-01: avatars ask Media for their drawn width, ovImgW); 1252.3 (2026-11-01: Following in the topics row, the picked topic remembered); 1250.2 (2026-11-01: topics live now, readable chat names on light themes); 1247.7 openvibe-shared 4.70.0 (2026-11-01: the app bar, busy(), skeletons, view transitions); 1244.3 with openvibe-shared 4.66.0: the mark with depth and motion, ov-fx loaded on request (2026-10-31); 1243.5 with openvibe-shared 4.64.0: the look travels between sites, the Look panel in the menu (2026-10-31); 1242.7 with openvibe-shared 4.59.0: the Pair mark beside the name, set in the brand face, and the ring on page moves (2026-10-31); 1238.8 with the home's featured stream once and the intro closed on phones (2026-10-31); 1237.4 with the home's two more tab blocks on phones (2026-10-30); 1233.9 with the home's Catch up tabs on phones (2026-10-30); 1230.8 with openvibe-shared 4.53.0: the footer's columns fold on phones (2026-10-30); 1227.8 with openvibe-shared 4.52.0: the Dock's loader and its Chats tab (2026-10-30); 1223.5 with openvibe-shared 4.48.0 (the site switcher on the mark, the menu's Start tab and quick tools; Kit's touch hit areas and the phone navbar fix, 4.44-4.47.3); 1215.4 with openvibe-shared 4.43.0 (room.css carries the kit tokens, 4.39.1; the kit's 4.39.0 rules); measured 1203.9 (2026-10-28: openvibe-shared 4.30.0, the bell center: tabs with counts, the Unread filter, thumbnails, swipe, the phone sheet; 4.31.0's ✦ adds about 3 KB); 1187.2 (2026-10-28: openvibe-shared 4.27.0, Notifications 2.0: opaque themed toasts held while a menu is open, grouping, priority, System cards and the rich bell panel); 1158.8 (2026-10-28: openvibe-shared 4.25.0, the menu's orb, messages, the phone sheet and auto-hide in the navbar; the messenger opens from the bar)
++ jsBrotliKB: 296.5, // 296.2 (2026-11-02: openvibe-shared 4.95.0, keyboard-reachable scrollers; the phone Pastes rail); 295.1 (2026-11-02: openvibe-shared 4.94.0, the Audio chip, the go-live card's labels); 294.6 (2026-11-02: content labels on cards and the player, L1); 294.1 (2026-11-01: the Catch up rails load by themselves as you swipe); 292.6 openvibe-shared 4.70.0 (2026-11-01: the app bar, busy(), skeletons, view transitions); 291.1 with openvibe-shared 4.57.1 (the Dock's async token and scope-aware controls, 4.57.0; the tool frame's facts, 4.56.0) (2026-10-31); 290.6 with the home's two more tab blocks on phones (2026-10-30); 289.8 with the home's Catch up tabs on phones (2026-10-30); 288.2 with openvibe-shared 4.52.0: the Dock's loader and its Chats tab (2026-10-30); 287.4 with openvibe-shared 4.48.0 (the site switcher on the mark, the menu's Start tab and quick tools; Kit's touch hit areas and the phone navbar fix, 4.44-4.47.3); measured 283.2 (2026-10-28: openvibe-shared 4.30.0, the bell center as above); 279.6 (2026-10-28: openvibe-shared 4.27.0, Notifications 2.0 as above); 273.1 (2026-10-28: openvibe-shared 4.25.0 as above); 270.5 (2026-10-28: openvibe-shared 4.22.0, the navbar's search magnifier with / and Ctrl+K and the More menu; search.js and loading.js load only when used); 269.6 (2026-10-28: openvibe-shared 4.19.0, the account menu's gear and people-group icons inline; 268 on 2026-10-27: openvibe-shared 4.16.0, the folded balance chip, Across OpenVibe with site marks and the icon preload in the navbar); 265.3 (2026-10-27: openvibe-shared 4.15.0, the compact footer opens into the whole one, and the pulse line); 263.3 on 10-23 (openvibe-shared 4.9.0, status text twins in every theme); 262.5 on 10-17 (openvibe-shared 4.4.0, every site in the launcher)
cssRawKB: 270, // measured ~243 since the first-paint subset (style.core.css) replaced style.css (~717 before)
cssBrotliKB: 55, // measured ~47.5 (was ~130)
};
ddiff --git a/test/home-catchup.test.js b/test/home-catchup.test.js
iindex ddaf9217..98b819ec 100644
---- a/test/home-catchup.test.js
++++ b/test/home-catchup.test.js
@@@ -49,8 +49,8 @@ check('no tabs and nothing that hides a rail on a phone', () => {
check('on a phone each rail is one row that scrolls sideways', () => {
const phone = css.slice(css.indexOf('/* Catch up on a phone'));
-- assert.match(phone, /#home-catchup \.stream-grid \{[^}]*grid-auto-flow: column;[^}]*overflow-x: auto;[^}]*scroll-snap-type: x mandatory;/);
-- assert.match(phone, /#home-catchup \.stream-grid > \* \{ scroll-snap-align: start; min-width: 0; \}/);
++ assert.match(phone, /#home-catchup \.stream-grid, #home-catchup \.home-pastes-list \{[^}]*grid-auto-flow: column;[^}]*overflow-x: auto;[^}]*scroll-snap-type: x mandatory;/, 'every rail, Recent Pastes too');
++ assert.match(phone, /#home-catchup \.stream-grid > \*, #home-catchup \.home-pastes-list > \* \{ scroll-snap-align: start; min-width: 0; \}/);
});
check('the changelog preview keeps its full height on a phone', () => {
ddiff --git a/test/home-rail-autoload.test.js b/test/home-rail-autoload.test.js
iindex ecd575e2..0d714ef0 100644
---- a/test/home-rail-autoload.test.js
++++ b/test/home-rail-autoload.test.js
@@@ -104,8 +104,14 @@ function page(overflowX) {
// The styles: the tail shows only in a sideways rail, the placeholders keep still for reduced motion, and the density
// control's column clamp never squeezes a rail's cards.
assert.match(css, /\.home-rail-tail \{ display: none; \}/);
-- assert.match(css, /#home-catchup \.stream-grid > \.home-rail-tail \{ display: flex; \}/);
++ assert.match(css, /#home-catchup \.stream-grid > \.home-rail-tail, #home-catchup \.home-pastes-list > \.home-rail-tail \{ display: flex; \}/);
assert.match(css, /@media \(prefers-reduced-motion: reduce\) \{ \.home-rail-skel \{ animation: none; \} \}/);
-- assert.match(css, /#home-catchup \.stream-grid\[data-ovd-cols\] \{ grid-template-columns: none !important; \}/);
++ assert.match(css, /#home-catchup \.stream-grid\[data-ovd-cols\], #home-catchup \.home-pastes-list\[data-ovd-cols\] \{ grid-template-columns: none !important; \}/);
++ // Recent Pastes rides the same rail on a phone (it was two cards a row with "Load 2 more", 2026-11-02).
++ assert.match(css, /#home-catchup \.stream-grid, #home-catchup \.home-pastes-list \{\n\s+display: grid; grid-auto-flow: column;/);
++ // A section passed over in a fling is shown: the observer counts "scrolled past", and a pause in scrolling sweeps.
++ const fx = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'home-fx.js'), 'utf8');
++ assert.ok(fx.includes('en.isIntersecting || en.boundingClientRect.bottom < 0'), 'scrolled past counts as seen');
++ assert.ok(/window\.addEventListener\('scroll', \(\) => \{\s+clearTimeout\(settle\);/.test(fx), 'a pause in scrolling shows what is still waiting');
console.log('home rail autoload: all checks passed');
})().catch((e) => { console.error(e); process.exit(1); });