openvibers/openvibe.network Commits
Keep account comfort display preferences (#423)
Changes
@@@ -151,12 +151,16 @@ router.get('/me/active', (req, res, next) => {res.json({ theme_id: prefs.theme_id, custom_variables: custom, display, override: false });});--// ── Display preferences (motion, text size) — follow the account to every site ──++// ── Display preferences — follow the account to every site ──// seasonal: the season's theme (openvibe-shared theme-loader Seasons: Pumpkin Night in October, Frost in December) in place// of the default theme, unless 'off' (the owner, 2026-10-24: "users can opt out … and just want their normal default").// scheme: 'manual' (the picked theme as it is) or 'auto' (its light or dark half, as the device is set; openvibe-shared// theme-loader Light / Dark / Auto, 2026-10-25).--const DISPLAY = { motion: ['auto', 'reduced'], text: ['100', '112', '125'], seasonal: ['on', 'off'], scheme: ['manual', 'auto'] };++// contrast: increase contrast for easier reading.++// font: use the readable typeface across sites.++// colors: choose colors designed to be safe for color blindness.++// oled: use pure black backgrounds on OLED displays.++const DISPLAY = { motion: ['auto', 'reduced'], text: ['100', '112', '125'], seasonal: ['on', 'off'], scheme: ['manual', 'auto'], contrast: ['normal', 'more'], font: ['default', 'readable'], colors: ['default', 'cb-safe'], oled: ['off', 'on'] };function cleanDisplay(d) {if (!d || typeof d !== 'object') return null;const out = {};@@@ -102,10 +102,13 @@ const token = async (id) => signToken(await db.prepare('SELECT * FROM users WHERassert.deepStrictEqual([r.status, r.body.theme.slug], [200, 'ember'], 'followed, it is the ember theme');assert.strictEqual((await call('GET', '/api/themes/no-such-theme')).status, 404, 'an unknown name is still a 404');-- // ── Display preferences follow the account: text size, motion and seasonal themes (on|off), nothing else ──-- r = await call('PUT', '/api/themes/me/display', { as: 2, body: { text: '112', motion: 'reduced', seasonal: 'off', colour: 'red' } });-- assert.deepStrictEqual(r.body.display, { motion: 'reduced', text: '112', seasonal: 'off' }, 'seasonal is kept, an unknown key is not');-- assert.deepStrictEqual((await call('GET', '/api/themes/me/active', { as: 2 })).body.display, { motion: 'reduced', text: '112', seasonal: 'off' });++ // ── Display preferences follow the account; only known keys and values are kept ──++ r = await call('PUT', '/api/themes/me/display', { as: 2, body: { text: '112', motion: 'reduced', seasonal: 'off', contrast: 'more', font: 'readable', colors: 'cb-safe', oled: 'on', colour: 'red' } });++ const display = { motion: 'reduced', text: '112', seasonal: 'off', contrast: 'more', font: 'readable', colors: 'cb-safe', oled: 'on' };++ assert.deepStrictEqual(r.body.display, display, 'comfort settings are kept, an unknown key is not');++ assert.deepStrictEqual((await call('GET', '/api/themes/me/active', { as: 2 })).body.display, display, 'saved comfort settings follow the account');++ r = await call('PUT', '/api/themes/me/display', { as: 2, body: { contrast: 'more', font: 'comic-sans' } });++ assert.deepStrictEqual(r.body.display, { contrast: 'more' }, 'an invalid font is dropped');r = await call('PUT', '/api/themes/me/display', { as: 2, body: { seasonal: 'maybe' } });assert.ok(!('seasonal' in (r.body.display || {})), 'only on or off');r = await call('PUT', '/api/themes/me/display', { as: 2, body: { scheme: 'auto' } });
@@ -151,12 +151,16 @@ router.get('/me/active', (req, res, next) => {
@@ -151,12 +151,16 @@ router.get('/me/active', (req, res, next) => {
res.json({ theme_id: prefs.theme_id, custom_variables: custom, display, override: false });
res.json({ theme_id: prefs.theme_id, custom_variables: custom, display, override: false });
});
});
-// ── Display preferences (motion, text size) — follow the account to every site ──
+// ── Display preferences — follow the account to every site ──
// seasonal: the season's theme (openvibe-shared theme-loader Seasons: Pumpkin Night in October, Frost in December) in place
// seasonal: the season's theme (openvibe-shared theme-loader Seasons: Pumpkin Night in October, Frost in December) in place
// of the default theme, unless 'off' (the owner, 2026-10-24: "users can opt out … and just want their normal default").
// of the default theme, unless 'off' (the owner, 2026-10-24: "users can opt out … and just want their normal default").
// scheme: 'manual' (the picked theme as it is) or 'auto' (its light or dark half, as the device is set; openvibe-shared
// scheme: 'manual' (the picked theme as it is) or 'auto' (its light or dark half, as the device is set; openvibe-shared
// theme-loader Light / Dark / Auto, 2026-10-25).
// theme-loader Light / Dark / Auto, 2026-10-25).
-const DISPLAY = { motion: ['auto', 'reduced'], text: ['100', '112', '125'], seasonal: ['on', 'off'], scheme: ['manual', 'auto'] };
+// contrast: increase contrast for easier reading.
+// font: use the readable typeface across sites.
+// colors: choose colors designed to be safe for color blindness.
+// oled: use pure black backgrounds on OLED displays.
+const DISPLAY = { motion: ['auto', 'reduced'], text: ['100', '112', '125'], seasonal: ['on', 'off'], scheme: ['manual', 'auto'], contrast: ['normal', 'more'], font: ['default', 'readable'], colors: ['default', 'cb-safe'], oled: ['off', 'on'] };
function cleanDisplay(d) {
function cleanDisplay(d) {
if (!d || typeof d !== 'object') return null;
if (!d || typeof d !== 'object') return null;
const out = {};
const out = {};
@@ -102,10 +102,13 @@ const token = async (id) => signToken(await db.prepare('SELECT * FROM users WHER
@@ -102,10 +102,13 @@ const token = async (id) => signToken(await db.prepare('SELECT * FROM users WHER
assert.deepStrictEqual([r.status, r.body.theme.slug], [200, 'ember'], 'followed, it is the ember theme');
assert.deepStrictEqual([r.status, r.body.theme.slug], [200, 'ember'], 'followed, it is the ember theme');
assert.strictEqual((await call('GET', '/api/themes/no-such-theme')).status, 404, 'an unknown name is still a 404');
assert.strictEqual((await call('GET', '/api/themes/no-such-theme')).status, 404, 'an unknown name is still a 404');
- // ── Display preferences follow the account: text size, motion and seasonal themes (on|off), nothing else ──
- r = await call('PUT', '/api/themes/me/display', { as: 2, body: { text: '112', motion: 'reduced', seasonal: 'off', colour: 'red' } });
- assert.deepStrictEqual(r.body.display, { motion: 'reduced', text: '112', seasonal: 'off' }, 'seasonal is kept, an unknown key is not');
- assert.deepStrictEqual((await call('GET', '/api/themes/me/active', { as: 2 })).body.display, { motion: 'reduced', text: '112', seasonal: 'off' });
+ // ── Display preferences follow the account; only known keys and values are kept ──
+ r = await call('PUT', '/api/themes/me/display', { as: 2, body: { text: '112', motion: 'reduced', seasonal: 'off', contrast: 'more', font: 'readable', colors: 'cb-safe', oled: 'on', colour: 'red' } });
+ const display = { motion: 'reduced', text: '112', seasonal: 'off', contrast: 'more', font: 'readable', colors: 'cb-safe', oled: 'on' };
+ assert.deepStrictEqual(r.body.display, display, 'comfort settings are kept, an unknown key is not');
+ assert.deepStrictEqual((await call('GET', '/api/themes/me/active', { as: 2 })).body.display, display, 'saved comfort settings follow the account');
+ r = await call('PUT', '/api/themes/me/display', { as: 2, body: { contrast: 'more', font: 'comic-sans' } });
+ assert.deepStrictEqual(r.body.display, { contrast: 'more' }, 'an invalid font is dropped');
r = await call('PUT', '/api/themes/me/display', { as: 2, body: { seasonal: 'maybe' } });
r = await call('PUT', '/api/themes/me/display', { as: 2, body: { seasonal: 'maybe' } });
assert.ok(!('seasonal' in (r.body.display || {})), 'only on or off');
assert.ok(!('seasonal' in (r.body.display || {})), 'only on or off');
r = await call('PUT', '/api/themes/me/display', { as: 2, body: { scheme: 'auto' } });
r = await call('PUT', '/api/themes/me/display', { as: 2, body: { scheme: 'auto' } });