modularize frontend and implement new css and themes

This commit is contained in:
2026-08-24 13:10:37 +01:00
parent 13116899db
commit 02b7ba317c
14 changed files with 1572 additions and 1768 deletions
+187
View File
@@ -0,0 +1,187 @@
var activeSessionId = null;
var supporterWs = null;
var lastTypingSessionId = null;
var supporterTypingActive = false;
function setAppHeight() {
var height = window.visualViewport ? window.visualViewport.height : window.innerHeight;
document.documentElement.style.setProperty('--app-height', height + 'px');
}
setAppHeight();
window.addEventListener('resize', setAppHeight);
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', setAppHeight);
window.visualViewport.addEventListener('scroll', setAppHeight);
}
function openSidebar() {
document.getElementById('sidebar').classList.add('open');
document.getElementById('drawer-backdrop').classList.add('visible');
}
function closeSidebar() {
document.getElementById('sidebar').classList.remove('open');
document.getElementById('drawer-backdrop').classList.remove('visible');
}
function selectSession(button) {
document.querySelectorAll('.session-item').forEach(function (b) { b.classList.remove('selected'); });
button.classList.add('selected');
setActiveSession(button.dataset.sessionId, button.dataset.sessionName);
closeSidebar();
}
function setActiveSession(sessionId, name) {
stopSupporterTyping(lastTypingSessionId);
activeSessionId = sessionId;
document.getElementById('active-session-name').textContent = name;
document.getElementById('active-session-name').style.color = '';
document.getElementById('active-session-meta').textContent = 'Session #' + sessionId.slice(0, 8);
document.getElementById('reply-area').classList.add('visible');
document.getElementById('reply-input').value = '';
hideUserTyping();
scrollSupporterMessages();
}
function resetSupporterDashboard() {
stopSupporterTyping(lastTypingSessionId);
activeSessionId = null;
document.getElementById('active-session-name').textContent = 'Select a conversation';
document.getElementById('active-session-name').style.color = 'var(--ink-dim)';
document.getElementById('active-session-meta').textContent = '';
document.getElementById('reply-area').classList.remove('visible');
hideUserTyping();
closeSidebar();
document.getElementById('supporter-messages').innerHTML =
'<div class="empty-state"><p>Chat history was cleared.</p><p>New conversations will appear in the chat list.</p></div>';
htmx.trigger('#session-list', 'refresh');
}
function scrollSupporterMessages() {
var el = document.getElementById('supporter-messages');
if (el) el.scrollTop = el.scrollHeight;
}
function showUserTyping() {
var indicator = document.getElementById('supporter-peer-typing');
if (!indicator) return;
indicator.classList.add('visible');
scrollSupporterMessages();
}
function hideUserTyping() {
var indicator = document.getElementById('supporter-peer-typing');
if (indicator) indicator.classList.remove('visible');
}
function sendSupporterTypingState(state, sessionId) {
sessionId = sessionId || activeSessionId;
if (!sessionId || !supporterWs || supporterWs.readyState !== WebSocket.OPEN) return;
supporterWs.send('typing:supporter:' + state + ':' + sessionId);
}
function startSupporterTyping() {
var input = document.getElementById('reply-input');
if (!activeSessionId || !input || !input.value.trim()) {
stopSupporterTyping(lastTypingSessionId || activeSessionId);
return;
}
if (supporterTypingActive && lastTypingSessionId === activeSessionId) return;
supporterTypingActive = true;
lastTypingSessionId = activeSessionId;
sendSupporterTypingState('start', activeSessionId);
}
function stopSupporterTyping(sessionId) {
sessionId = sessionId || activeSessionId;
if (!supporterTypingActive && !sessionId) return;
var targetSession = sessionId || lastTypingSessionId;
if (targetSession) sendSupporterTypingState('stop', targetSession);
supporterTypingActive = false;
lastTypingSessionId = null;
}
function handleReplyInput() {
var input = document.getElementById('reply-input');
if (input && input.value.trim()) startSupporterTyping();
else stopSupporterTyping(lastTypingSessionId || activeSessionId);
}
document.addEventListener('htmx:afterSwap', function (e) {
if (e.detail.target.id === 'supporter-messages') scrollSupporterMessages();
});
document.getElementById('reply-btn').addEventListener('click', function () {
if (!activeSessionId) return;
var input = document.getElementById('reply-input');
var content = input.value.trim();
if (!content) return;
stopSupporterTyping(activeSessionId);
fetch('/supporter/send/' + activeSessionId, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: 'content=' + encodeURIComponent(content)
})
.then(function (r) { return r.text(); })
.then(function (html) {
var msgs = document.getElementById('supporter-messages');
msgs.insertAdjacentHTML('beforeend', html);
input.value = '';
scrollSupporterMessages();
setAppHeight();
});
});
document.getElementById('reply-input').addEventListener('keydown', function (e) {
if (e.key === 'Enter') document.getElementById('reply-btn').click();
});
document.getElementById('reply-input').addEventListener('input', handleReplyInput);
document.getElementById('reply-input').addEventListener('focus', function () { setTimeout(setAppHeight, 80); });
document.getElementById('reply-input').addEventListener('blur', function () { setTimeout(setAppHeight, 80); });
function connectSupporterWS() {
var wsProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
supporterWs = new WebSocket(wsProtocol + '://' + location.host + '/ws/supporter/dashboard');
supporterWs.onmessage = function (e) {
var data = e.data;
if (data === '__reset__') {
resetSupporterDashboard();
return;
}
if (data.indexOf('typing:user:start:') === 0) {
var sessionId = data.replace('typing:user:start:', '');
if (sessionId === activeSessionId) showUserTyping();
return;
}
if (data.indexOf('typing:user:stop:') === 0) {
var sessionId2 = data.replace('typing:user:stop:', '');
if (sessionId2 === activeSessionId) hideUserTyping();
return;
}
if (data.indexOf('typing:supporter:') === 0) return;
htmx.trigger('#session-list', 'refresh');
if (data === activeSessionId) {
hideUserTyping();
fetch('/supporter/messages/' + activeSessionId)
.then(function (r) { return r.text(); })
.then(function (html) {
document.getElementById('supporter-messages').innerHTML = html;
scrollSupporterMessages();
});
}
};
supporterWs.onopen = function () {
if (supporterTypingActive && lastTypingSessionId) sendSupporterTypingState('start', lastTypingSessionId);
};
supporterWs.onclose = function () { setTimeout(connectSupporterWS, 2000); };
}
connectSupporterWS();