Compare commits
7 Commits
a434f11561
...
191a368258
| Author | SHA1 | Date | |
|---|---|---|---|
| 191a368258 | |||
| 7a10206617 | |||
| 8b96f4dc8a | |||
| 4666986f78 | |||
| 5e002004cc | |||
| d3fb0eacb6 | |||
| 7bcb670b96 |
@@ -89,8 +89,9 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
display: none;
|
display: none;
|
||||||
z-index: 1000;
|
z-index: 3000;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.server-card {
|
.server-card {
|
||||||
@@ -241,69 +242,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Conversation View Styles */
|
/* Conversation View Styles */
|
||||||
.conversation-view {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversations-list {
|
|
||||||
max-height: 600px;
|
|
||||||
overflow-y: auto;
|
|
||||||
border: 1px solid #444;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 1rem;
|
|
||||||
background: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-message {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding: 0.75rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border-left: 4px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-message.user-message {
|
|
||||||
background: #2a2a3a;
|
|
||||||
border-left-color: #4CAF50;
|
|
||||||
}
|
|
||||||
|
|
||||||
.conversation-message.bot-message {
|
|
||||||
background: #3a2a2a;
|
|
||||||
border-left-color: #2196F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sender {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timestamp {
|
|
||||||
color: #999;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-content {
|
|
||||||
color: #ddd;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-attachments {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
padding: 0.5rem;
|
|
||||||
background: rgba(255,255,255,0.05);
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-reactions {
|
.message-reactions {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1908,21 +1846,59 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}, { passive: false });
|
}, { passive: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up periodic updates
|
// Set up periodic updates with visibility-aware polling
|
||||||
setInterval(loadStatus, 10000);
|
let statusInterval, logsInterval, argsInterval;
|
||||||
setInterval(loadLogs, 5000);
|
|
||||||
setInterval(loadActiveArguments, 5000); // Refresh active arguments
|
function startPolling() {
|
||||||
|
if (!statusInterval) statusInterval = setInterval(loadStatus, 10000);
|
||||||
|
if (!logsInterval) logsInterval = setInterval(loadLogs, 5000);
|
||||||
|
if (!argsInterval) argsInterval = setInterval(loadActiveArguments, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPolling() {
|
||||||
|
clearInterval(statusInterval); statusInterval = null;
|
||||||
|
clearInterval(logsInterval); logsInterval = null;
|
||||||
|
clearInterval(argsInterval); argsInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
stopPolling();
|
||||||
|
console.log('⏸ Tab hidden — polling paused');
|
||||||
|
} else {
|
||||||
|
loadStatus(); loadLogs(); loadActiveArguments();
|
||||||
|
startPolling();
|
||||||
|
console.log('▶️ Tab visible — polling resumed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
startPolling();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
let notificationTimer = null;
|
||||||
|
|
||||||
function showNotification(message, type = 'info') {
|
function showNotification(message, type = 'info') {
|
||||||
const notification = document.getElementById('notification');
|
const notification = document.getElementById('notification');
|
||||||
notification.textContent = message;
|
notification.textContent = message;
|
||||||
notification.style.display = 'block';
|
notification.style.display = 'block';
|
||||||
notification.style.backgroundColor = type === 'error' ? '#d32f2f' : '#222';
|
notification.style.opacity = '0.95';
|
||||||
|
|
||||||
setTimeout(() => {
|
if (type === 'error') {
|
||||||
notification.style.display = 'none';
|
notification.style.backgroundColor = '#d32f2f';
|
||||||
|
} else if (type === 'success') {
|
||||||
|
notification.style.backgroundColor = '#2e7d32';
|
||||||
|
} else {
|
||||||
|
notification.style.backgroundColor = '#222';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notificationTimer) clearTimeout(notificationTimer);
|
||||||
|
notificationTimer = setTimeout(() => {
|
||||||
|
notification.style.opacity = '0';
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.style.display = 'none';
|
||||||
|
notificationTimer = null;
|
||||||
|
}, 300);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2521,12 +2497,15 @@ async function updateBedtimeRange(guildId) {
|
|||||||
const [startHour, startMinute] = startTime.split(':').map(Number);
|
const [startHour, startMinute] = startTime.split(':').map(Number);
|
||||||
const [endHour, endMinute] = endTime.split(':').map(Number);
|
const [endHour, endMinute] = endTime.split(':').map(Number);
|
||||||
|
|
||||||
|
const button = document.querySelector(`button[onclick="updateBedtimeRange('${guildIdStr}')"]`);
|
||||||
|
const originalText = button ? button.textContent : 'Update Bedtime Range';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Show loading state
|
// Show loading state
|
||||||
const button = document.querySelector(`button[onclick="updateBedtimeRange('${guildIdStr}')"]`);
|
if (button) {
|
||||||
const originalText = button.textContent;
|
button.textContent = 'Updating...';
|
||||||
button.textContent = 'Updating...';
|
button.disabled = true;
|
||||||
button.disabled = true;
|
}
|
||||||
|
|
||||||
// Send the update request
|
// Send the update request
|
||||||
const response = await fetch(`/servers/${guildIdStr}/bedtime-range`, {
|
const response = await fetch(`/servers/${guildIdStr}/bedtime-range`, {
|
||||||
@@ -2556,9 +2535,8 @@ async function updateBedtimeRange(guildId) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update bedtime range:', error);
|
console.error('Failed to update bedtime range:', error);
|
||||||
showNotification(error.message || 'Failed to update bedtime range', 'error');
|
showNotification(error.message || 'Failed to update bedtime range', 'error');
|
||||||
|
} finally {
|
||||||
// Restore button state
|
// Restore button state
|
||||||
const button = document.querySelector(`button[onclick="updateBedtimeRange('${guildIdStr}')"]`);
|
|
||||||
if (button) {
|
if (button) {
|
||||||
button.textContent = originalText;
|
button.textContent = originalText;
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
@@ -4064,6 +4042,7 @@ function scrollLogsToBottom() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(text) {
|
function escapeHtml(text) {
|
||||||
|
if (!text) return '';
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.textContent = text;
|
div.textContent = text;
|
||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
@@ -5045,12 +5024,15 @@ function addChatMessage(sender, content, isError = false) {
|
|||||||
|
|
||||||
messageDiv.innerHTML = `
|
messageDiv.innerHTML = `
|
||||||
<div class="chat-message-header">
|
<div class="chat-message-header">
|
||||||
<span class="chat-message-sender">${sender}</span>
|
<span class="chat-message-sender">${escapeHtml(sender)}</span>
|
||||||
<span class="chat-message-time">${timestamp}</span>
|
<span class="chat-message-time">${timestamp}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="chat-message-content">${content}</div>
|
<div class="chat-message-content"></div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Set content via textContent to prevent XSS
|
||||||
|
messageDiv.querySelector('.chat-message-content').textContent = content;
|
||||||
|
|
||||||
chatMessages.appendChild(messageDiv);
|
chatMessages.appendChild(messageDiv);
|
||||||
|
|
||||||
// Scroll to bottom
|
// Scroll to bottom
|
||||||
@@ -5863,6 +5845,37 @@ function closeCreateMemoryModal() {
|
|||||||
document.getElementById('create-memory-modal').style.display = 'none';
|
document.getElementById('create-memory-modal').style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modal keyboard and backdrop close handlers
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
const editModal = document.getElementById('edit-memory-modal');
|
||||||
|
const createModal = document.getElementById('create-memory-modal');
|
||||||
|
if (editModal && editModal.style.display !== 'none') closeEditMemoryModal();
|
||||||
|
if (createModal && createModal.style.display !== 'none') closeCreateMemoryModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const editModal = document.getElementById('edit-memory-modal');
|
||||||
|
const createModal = document.getElementById('create-memory-modal');
|
||||||
|
if (editModal) {
|
||||||
|
editModal.setAttribute('role', 'dialog');
|
||||||
|
editModal.setAttribute('aria-modal', 'true');
|
||||||
|
editModal.setAttribute('aria-label', 'Edit Memory');
|
||||||
|
editModal.addEventListener('click', function(e) {
|
||||||
|
if (e.target === this) closeEditMemoryModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (createModal) {
|
||||||
|
createModal.setAttribute('role', 'dialog');
|
||||||
|
createModal.setAttribute('aria-modal', 'true');
|
||||||
|
createModal.setAttribute('aria-label', 'Create Memory');
|
||||||
|
createModal.addEventListener('click', function(e) {
|
||||||
|
if (e.target === this) closeCreateMemoryModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function saveNewMemory() {
|
async function saveNewMemory() {
|
||||||
const collection = document.getElementById('create-memory-collection').value;
|
const collection = document.getElementById('create-memory-collection').value;
|
||||||
const content = document.getElementById('create-memory-content').value.trim();
|
const content = document.getElementById('create-memory-content').value.trim();
|
||||||
@@ -5930,13 +5943,6 @@ function filterMemories(listId, searchTerm) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(str) {
|
|
||||||
if (!str) return '';
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.textContent = str;
|
|
||||||
return div.innerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
function escapeJsonForAttribute(obj) {
|
function escapeJsonForAttribute(obj) {
|
||||||
// Properly escape JSON for use in HTML attributes
|
// Properly escape JSON for use in HTML attributes
|
||||||
return JSON.stringify(obj)
|
return JSON.stringify(obj)
|
||||||
|
|||||||
Reference in New Issue
Block a user