// Lotus Chat in-game voice overlay (cinny-desktop #25). // // Rust (native/overlay.rs) calls window.lotusOverlay(state, maxRows) with the // call snapshot from the web app. Everything is rendered with textContent / // createElement, never markup strings, so a display name can't inject HTML. 'use strict'; (function () { var panel = document.getElementById('panel'); var selfRow = document.getElementById('self'); var list = document.getElementById('people'); // Stable colour per user, from the user ID. var COLOURS = ['#7c5cff', '#2f9e8f', '#d9822b', '#c2417c', '#3d7be0', '#5aa02c', '#a35bd6', '#c94f4f']; function colourFor(id) { var h = 0; for (var i = 0; i < id.length; i += 1) h = (h * 31 + id.charCodeAt(i)) >>> 0; return COLOURS[h % COLOURS.length]; } function initial(name) { var trimmed = (name || '').replace(/^[@#!+]/, '').trim(); var first = Array.from(trimmed)[0]; return first ? first.toUpperCase() : '?'; } function el(tag, cls, text) { var node = document.createElement(tag); if (cls) node.className = cls; if (text !== undefined) node.textContent = text; return node; } function badge(text, off) { return el('span', off ? 'badge off' : 'badge', text); } function renderSelf(state) { selfRow.replaceChildren(); selfRow.appendChild(el('span', '', 'Lotus voice')); if (state.deafened) selfRow.appendChild(badge('Deafened', true)); else if (state.muted) selfRow.appendChild(badge('Muted', true)); if (state.ptt) selfRow.appendChild(badge('PTT', false)); } function renderPerson(p) { var row = el('li', 'person'); if (p.speaking) row.classList.add('speaking'); if (p.muted) row.classList.add('muted'); var avatar = el('span', 'avatar', initial(p.name)); avatar.style.background = colourFor(p.userId || p.name || ''); row.appendChild(avatar); row.appendChild(el('span', 'name', p.isSelf ? p.name + ' (you)' : p.name)); if (p.muted) row.appendChild(el('span', 'mic', 'Muted')); return row; } window.lotusOverlay = function (state, maxRows) { if (!state || !state.active) { panel.hidden = true; return; } renderSelf(state); list.replaceChildren(); var people = Array.isArray(state.people) ? state.people : []; var limit = typeof maxRows === 'number' ? maxRows : 10; people.slice(0, limit).forEach(function (p) { list.appendChild(renderPerson(p)); }); if (people.length > limit) { list.appendChild(el('li', 'more', '+' + (people.length - limit) + ' more')); } panel.hidden = false; }; })();