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);
});