openvibers/openvibe.live Commits

Chat names and the AI label readable on light themes (#521)

OP OpenVibers committed 12 days ago · 1bd65847fb · 3 files, +20 −8

* Chat names and the AI label readable on light themes

* Budget: 1250.2 KB of JS (topics, readable names on light themes)

Changes

ddiff --git a/public/css/features/content.css b/public/css/features/content.css
iindex 40f18dd7..95a2305e 100644
---- a/public/css/features/content.css
++++ b/public/css/features/content.css
@@@ -6,7 +6,8 @@
.cf-wrap {
--cf-gap: 16px;
--cf-radius: 14px;
-- --cf-ai: #a78bfa; /* AI text on dark surfaces */
++ /* AI text: the violet mixed with the theme's text, so it holds on light themes too (it read 2.5:1 on Daylight). */
++ --cf-ai: color-mix(in srgb, #a78bfa 62%, var(--text-primary, #e6edf7));
--cf-ai-2: #22d3ee;
--cf-ai-fill: linear-gradient(135deg, #6d28d9, #0e7490); /* behind white text: 4.5:1 or better */
max-width: 1240px;
ddiff --git a/public/js/chat.js b/public/js/chat.js
iindex 0257bd30..cd2802e0 100644
---- a/public/js/chat.js
++++ b/public/js/chat.js
@@@ -1138,7 +1138,7 @@ function queueFullscreenChatEntry(entry) {
function buildFullscreenChatEntry(msg) {
const badge = chatSettings.showBadges ? getBadgeHTML(msg.role) : '';
let nameColor = relayColorFor(msg) || msg.color || msg.profile_color || getRoleColor(msg.role);
-- if (chatSettings.readableColors) nameColor = ensureReadableColor(nameColor);
++ if (chatSettings.readableColors || _chatLightTheme()) nameColor = ensureReadableColor(nameColor);
const displayName = esc(msg.username || msg.displayName || `anon${msg.anonId || ''}`);
const _relayPlatform = (msg.source_platform || parseRelayUsername(msg.username || msg.displayName || '').platform || '').toLowerCase();
const _relayBadge = relayBadgeHTML(_relayPlatform);
@@@ -2586,7 +2586,7 @@ function buildChatMessageEl(msg, opts = {}) {
const badge = chatSettings.showBadges ? getBadgeHTML(msg.role) : '';
let nameColor = relayColorFor(msg) || msg.color || msg.profile_color || getRoleColor(msg.role);
// Readable colors — ensure minimum contrast against dark backgrounds
-- if (chatSettings.readableColors) nameColor = ensureReadableColor(nameColor);
++ if (chatSettings.readableColors || _chatLightTheme()) nameColor = ensureReadableColor(nameColor);
const displayName = esc(msg.username || msg.displayName || `anon${msg.anonId || ''}`);
const coreUsername = esc(msg.core_username || '');
@@@ -2906,7 +2906,7 @@ function addSystemMessage(text, isError = false) {
*/
function getChatAvatarHTML(msg) {
let nameColor = relayColorFor(msg) || msg.color || msg.profile_color || getRoleColor(msg.role);
-- if (chatSettings.readableColors) nameColor = ensureReadableColor(nameColor);
++ if (chatSettings.readableColors || _chatLightTheme()) nameColor = ensureReadableColor(nameColor);
// Strip any "[Twitch]/[Kick]/[RS]" relay prefix so the initial is the real letter.
const raw = (msg.username || msg.displayName || `anon${msg.anonId || ''}`).replace(/^\[[^\]]+\]\s*/, '');
const initial = esc((raw.charAt(0) || '?').toUpperCase());
@@@ -2927,7 +2927,7 @@ function addGottiMessage(msg) {
const avatarHtml = getChatAvatarHTML(msg);
const hatHtml = msg.hatFX?.emoji ? `<span class="chat-hat" aria-hidden="true">${esc(msg.hatFX.emoji)}</span>` : '';
let nameColor = relayColorFor(msg) || msg.color || msg.profile_color || getRoleColor(msg.role);
-- if (chatSettings.readableColors) nameColor = ensureReadableColor(nameColor);
++ if (chatSettings.readableColors || _chatLightTheme()) nameColor = ensureReadableColor(nameColor);
const displayName = esc(msg.username || msg.displayName || `anon${msg.anonId || ''}`);
const coreUsername = esc(msg.core_username || '');
@@@ -5203,7 +5203,7 @@ function _buildCrossfeedEl(msg) {
}
const badge = chatSettings.showBadges ? getBadgeHTML(msg.role) : '';
let nameColor = relayColorFor(msg) || msg.color || msg.profile_color || getRoleColor(msg.role);
-- if (chatSettings.readableColors) nameColor = ensureReadableColor(nameColor);
++ if (chatSettings.readableColors || _chatLightTheme()) nameColor = ensureReadableColor(nameColor);
const displayName = esc(msg.username || msg.displayName || `anon${msg.anonId || ''}`);
const _relayPlatform = (msg.source_platform || parseRelayUsername(msg.username || msg.displayName || '').platform || '').toLowerCase();
const _relayBadge = relayBadgeHTML(_relayPlatform);
@@@ -5961,8 +5961,13 @@ function playMentionSound() {
} catch { }
}
++// A light theme (theme-loader sets data-theme-mode): pale name colours there vanish on white, so names are always made
++// readable, darkened rather than brightened (contrast probe, 2026-11-01: 1.56:1 for a pale name on Daylight).
++function _chatLightTheme() {
++ try { return document.documentElement.getAttribute('data-theme-mode') === 'light'; } catch { return false; }
++}
function ensureReadableColor(hex) {
-- // Ensure minimum luminance for dark backgrounds (target: 0.4 relative luminance)
++ // Dark themes: a minimum brightness (0.4). Light themes: a maximum (0.45), about 4.5:1 on white.
try {
let r, g, b;
if (hex.startsWith('#')) {
@@@ -5974,6 +5979,12 @@ function ensureReadableColor(hex) {
return hex;
}
const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
++ if (_chatLightTheme()) {
++ if (lum <= 0.45) return hex;
++ const k = 0.45 / lum;
++ r = Math.round(r * k); g = Math.round(g * k); b = Math.round(b * k);
++ return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
++ }
if (lum < 0.4) {
// Brighten the color while keeping the hue
const boost = 0.4 / Math.max(lum, 0.01);
ddiff --git a/scripts/perf/check-budgets.js b/scripts/perf/check-budgets.js
iindex 2da7fa4c..42d3cff0 100644
---- a/scripts/perf/check-budgets.js
++++ b/scripts/perf/check-budgets.js
@@@ -22,7 +22,7 @@ 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: 1250, // 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)
++ jsRawKB: 1251, // 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: 293.3, // 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)