diff --git a/webapp/templates/shoutbox-popout.html b/webapp/templates/shoutbox-popout.html index 17d2922..9ec0952 100644 --- a/webapp/templates/shoutbox-popout.html +++ b/webapp/templates/shoutbox-popout.html @@ -85,6 +85,8 @@ function userColor(name) { return colorMap[name]; } +var currentUser = '{{ current_user.username }}'; + function esc(t) { var d = document.createElement('div'); d.textContent = t; return d.innerHTML; } function fmtTime(iso) { @@ -92,6 +94,22 @@ function fmtTime(iso) { return d.getHours().toString().padStart(2,'0') + ':' + d.getMinutes().toString().padStart(2,'0'); } +function mentionUser(username) { + var input = document.getElementById('shoutboxInput'); + var val = input.value; + var mention = '@' + username + ' '; + if (val && !val.endsWith(' ')) mention = ' ' + mention; + input.value = val + mention; + input.focus(); +} + +function renderText(text) { + var escaped = esc(text); + var mentioned = currentUser && text.toLowerCase().indexOf('@' + currentUser.toLowerCase()) >= 0; + escaped = escaped.replace(/@(\w+)/g, '@$1'); + return { html: escaped, mentioned: mentioned }; +} + function addItem(item) { if (knownIds.has(item.id)) return; knownIds.add(item.id); @@ -100,12 +118,16 @@ function addItem(item) { if (ph) ph.remove(); var line = document.createElement('div'); - line.className = 'py-0.5 leading-relaxed'; + line.className = 'py-0.5 leading-relaxed rounded px-1'; var time = '[' + fmtTime(item.created_at) + '] '; if (item.type === 'message') { var c = userColor(item.author); - line.innerHTML = time + '<' + esc(item.author) + '> ' + esc(item.text) + ''; + var rendered = renderText(item.text); + line.innerHTML = time + '<' + esc(item.author) + '> ' + rendered.html + ''; + if (rendered.mentioned) { + line.className += ' bg-red-900/40 font-bold'; + } } else { var au = (item.action || '').toUpperCase(); var sc = 'text-red-400'; @@ -135,7 +157,8 @@ function updateOnline(users) { list.innerHTML = ''; users.forEach(function(u) { var el = document.createElement('div'); - el.className = 'flex items-center gap-1.5 text-xs text-gray-300'; + el.className = 'flex items-center gap-1.5 text-xs text-gray-300 cursor-pointer hover:text-white'; + el.onclick = function() { mentionUser(u); }; el.innerHTML = '' + esc(u); list.appendChild(el); }); diff --git a/webapp/templates/twitch-moderation.html b/webapp/templates/twitch-moderation.html index 0644b9b..24ec397 100644 --- a/webapp/templates/twitch-moderation.html +++ b/webapp/templates/twitch-moderation.html @@ -979,6 +979,7 @@ document.addEventListener('keydown', function(e) { // ============================= // Shoutbox modérateurs (IRC) // ============================= +var shoutboxCurrentUser = '{{ current_user.username }}'; var shoutboxKnownIds = new Set(); var shoutboxLastTimestamp = ''; var shoutboxAutoScroll = true; @@ -1025,6 +1026,23 @@ function shoutboxTime(isoStr) { return d.getHours().toString().padStart(2, '0') + ':' + d.getMinutes().toString().padStart(2, '0'); } +function shoutboxMention(username) { + var input = document.getElementById('shoutboxInput'); + var val = input.value; + var mention = '@' + username + ' '; + if (val && !val.endsWith(' ')) mention = ' ' + mention; + input.value = val + mention; + input.focus(); +} + +function shoutboxRenderText(text) { + var escaped = escapeHtml(text); + var isMentioned = shoutboxCurrentUser && text.toLowerCase().indexOf('@' + shoutboxCurrentUser.toLowerCase()) >= 0; + escaped = escaped.replace(/@(\w+)/g, '@$1'); + if (isMentioned) return { html: escaped, mentioned: true }; + return { html: escaped, mentioned: false }; +} + function addShoutboxItem(item) { if (shoutboxKnownIds.has(item.id)) return; shoutboxKnownIds.add(item.id); @@ -1034,15 +1052,19 @@ function addShoutboxItem(item) { if (ph) ph.remove(); var line = document.createElement('div'); - line.className = 'py-0.5 leading-relaxed'; + line.className = 'py-0.5 leading-relaxed rounded px-1'; var time = '[' + shoutboxTime(item.created_at) + '] '; if (item.type === 'message') { var color = shoutboxColor(item.author); + var rendered = shoutboxRenderText(item.text); line.innerHTML = time + - '<' + escapeHtml(item.author) + '> ' + - '' + escapeHtml(item.text) + ''; + '<' + escapeHtml(item.author) + '> ' + + '' + rendered.html + ''; + if (rendered.mentioned) { + line.className += ' bg-red-900/40 font-bold'; + } } else { var actionUpper = (item.action || '').toUpperCase(); var sanctionColor = 'text-red-400'; @@ -1052,7 +1074,7 @@ function addShoutboxItem(item) { var text = '*** ' + actionUpper; if (item.moderator) text += ' par ' + item.moderator; - if (item.target) text += ' → ' + item.target; + if (item.target) text += ' \u2192 ' + item.target; if (item.details && item.details !== '-') text += ' (' + item.details + ')'; text += ' ***'; @@ -1077,7 +1099,8 @@ function updateOnlineList(users) { list.innerHTML = ''; users.forEach(function(u) { var el = document.createElement('div'); - el.className = 'flex items-center gap-1.5 text-xs text-gray-300'; + el.className = 'flex items-center gap-1.5 text-xs text-gray-300 cursor-pointer hover:text-white'; + el.onclick = function() { shoutboxMention(u); }; el.innerHTML = '' + escapeHtml(u); list.appendChild(el); });