openvibers/openvibe.chat Commits

openvibe.chat names: a person's colour is mixed half-and-half with the theme's text colour so it reads on any background (raw colours were below 4.5:1 on the dark panel), and a room line's colour is checked as a hex colour before it reaches the style attribute — WS-T task 3

OP OpenVibers committed 25 Sept 2026 · c16a83d99f · 3 files, +8 −5


Changes

ddiff --git a/public/web/chat.css b/public/web/chat.css
iindex 276a486..dc3692e 100644
---- a/public/web/chat.css
++++ b/public/web/chat.css
@@@ -40,7 +40,10 @@ h1 { font-size: 1.6rem; line-height: 1.25; margin: .3em 0 .2em; }
.oc-compact .oc-msg { padding: 1px 10px; }
.oc-time { display: none; color: var(--oc-muted); font-size: .8em; font-variant-numeric: tabular-nums; margin-right: 4px; }
.oc-times .oc-time { display: inline; }
--.oc-name { font-weight: 700; text-decoration: none; }
++.oc-name { font-weight: 700; text-decoration: none; color: var(--nc, inherit); }
++/* A person's own colour (--nc) mixed half-and-half with the theme's text colour: the hue stays theirs,
++ and it reads on any theme's background (their raw colour was below 4.5:1 on the dark panel). */
++.oc-name { color: color-mix(in oklab, var(--nc, var(--oc-text)) 50%, var(--oc-text)); }
.oc-anon { color: var(--oc-muted); font-weight: 600; }
.oc-where { color: var(--oc-muted); font-size: .8em; text-decoration: none; margin-left: 4px; }
.oc-text { white-space: pre-wrap; }
ddiff --git a/public/web/chat.js b/public/web/chat.js
iindex 66d953f..7c8c16e 100644
---- a/public/web/chat.js
++++ b/public/web/chat.js
@@@ -58,7 +58,7 @@
var handle = m.core_username || (m.user_id ? m.username : null);
var name = m.display_name || m.username || m.anon_id || 'someone';
var who;
-- if (handle) { who = el('a', 'oc-name', name); who.href = LIVE + '/@' + encodeURIComponent(handle); if (/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '')) who.style.color = m.profile_color; }
++ if (handle) { who = el('a', 'oc-name', name); who.href = LIVE + '/@' + encodeURIComponent(handle); if (/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '')) who.style.setProperty('--nc', m.profile_color); }
else who = el('span', 'oc-name oc-anon', name);
li.appendChild(who);
var b = badge(m.role); if (b) li.appendChild(b);
@@@ -167,7 +167,7 @@
li.appendChild(clock(m.created_at)); li.appendChild(document.createTextNode(' '));
var who = el('a', 'oc-name', m.display_name || m.username || 'someone');
who.href = LIVE + '/@' + encodeURIComponent(m.username || '');
-- if (/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '')) who.style.color = m.profile_color;
++ if (/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '')) who.style.setProperty('--nc', m.profile_color);
li.appendChild(who);
var b = badge(m.user_role); if (b) li.appendChild(b);
li.appendChild(document.createTextNode(' '));
ddiff --git a/server/web/pages.js b/server/web/pages.js
iindex d2b5771..f9fa1bf 100644
---- a/server/web/pages.js
++++ b/server/web/pages.js
@@@ -69,7 +69,7 @@ function globalMessage(m) {
const t = hhmm(m.timestamp);
const handle = m.core_username || (m.user_id ? m.username : null);
const name = esc(m.display_name || m.username || m.anon_id || 'someone');
-- const who = handle ? `<a class="oc-name" href="${LIVE}/@${encodeURIComponent(handle)}" style="color:${esc(/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '') ? m.profile_color : 'inherit')}">${name}</a>` : `<span class="oc-name oc-anon">${name}</span>`;
++ const who = handle ? `<a class="oc-name" href="${LIVE}/@${encodeURIComponent(handle)}" style="--nc:${esc(/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '') ? m.profile_color : 'inherit')}">${name}</a>` : `<span class="oc-name oc-anon">${name}</span>`;
const where = m.stream_channel ? ` <a class="oc-where" href="${LIVE}/@${encodeURIComponent(m.stream_channel)}" title="Sent from ${esc(m.stream_channel)}'s channel">@${esc(m.stream_channel)}</a>` : '';
return `<li class="oc-msg" data-id="${Number(m.id) || 0}"><time class="oc-time" datetime="${t.iso}">${t.text}</time> ${who}${roleBadge(m.role)}${where} <span class="oc-text">${linkify(m.message)}</span></li>`;
}
@@@ -312,7 +312,7 @@ ${volume('tts_sound_volume', 'Chat sound volume', tts.sound_volume)}
const roomMessage = (m, can, slug, me) => {
const t = hhmm(m.created_at);
const del = (can.moderate || (me && m.user_id === me.id)) ? `<form class="oc-del" method="post" action="/r/${esc(slug)}/delete/${Number(m.id)}"><button type="submit" title="Delete this message" aria-label="Delete this message">×</button></form>` : '';
-- return `<li class="oc-msg" data-id="${Number(m.id) || 0}" data-user="${Number(m.user_id) || 0}"><time class="oc-time" datetime="${t.iso}">${t.text}</time> <a class="oc-name" href="${LIVE}/@${encodeURIComponent(m.username || '')}"${/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '') ? ` style="color:${esc(m.profile_color)}"` : ''}>${esc(m.display_name || m.username || 'someone')}</a>${roleBadge(m.user_role)} <span class="oc-text">${linkify(m.message)}</span>${del}</li>`;
++ return `<li class="oc-msg" data-id="${Number(m.id) || 0}" data-user="${Number(m.user_id) || 0}"><time class="oc-time" datetime="${t.iso}">${t.text}</time> <a class="oc-name" href="${LIVE}/@${encodeURIComponent(m.username || '')}"${/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '') ? ` style="--nc:${/^#[0-9a-f]{3,8}$/i.test(m.profile_color || '') ? esc(m.profile_color) : 'inherit'}"` : ''}>${esc(m.display_name || m.username || 'someone')}</a>${roleBadge(m.user_role)} <span class="oc-text">${linkify(m.message)}</span>${del}</li>`;
};
router.get('/rooms', async (req, res) => {