From 7cb21a372baf7f72fbcd9a2f47812aa5ce84f6fe Mon Sep 17 00:00:00 2001 From: koko210Serve Date: Mon, 18 May 2026 21:43:44 +0300 Subject: [PATCH] feat: make Web UI mood dropdowns Evil Mode-aware - Disable per-server mood controls when Evil Miku is active - Show explanatory notice for disabled server mood dropdowns - Populate global mood dropdown with evil moods when Evil Mode is on - Fix initialization race condition by awaiting evil mode status first - Add CSS styles for disabled mood controls --- bot/static/css/style.css | 17 ++++++ bot/static/js/core.js | 9 ++-- bot/static/js/modes.js | 3 ++ bot/static/js/servers.js | 111 ++++++++++++++++++++++++++++++--------- 4 files changed, 112 insertions(+), 28 deletions(-) diff --git a/bot/static/css/style.css b/bot/static/css/style.css index a13d57c..7b38ceb 100644 --- a/bot/static/css/style.css +++ b/bot/static/css/style.css @@ -915,3 +915,20 @@ body.evil-mode [style*="color: rgb(0, 123, 255)"] { color: #888; margin-left: auto; } + +/* Disabled mood controls (Evil Miku per-server mood lockout) */ +.server-mood-controls select:disabled, +.server-mood-controls button:disabled { + opacity: 0.4; + cursor: not-allowed; + pointer-events: none; + background: #2a2a2a; + border-color: #444; +} +.evil-mode-exempt-notice { + color: #888; + font-size: 0.85rem; + font-style: italic; + margin-top: 0.5rem; + line-height: 1.4; +} diff --git a/bot/static/js/core.js b/bot/static/js/core.js index 0c1435c..aadb863 100644 --- a/bot/static/js/core.js +++ b/bot/static/js/core.js @@ -383,7 +383,7 @@ async function loadProfilePictureMetadata() { // DOMContentLoaded — main initialization // ============================================================================ -document.addEventListener('DOMContentLoaded', function() { +document.addEventListener('DOMContentLoaded', async function() { initTabState(); initTabWheelScroll(); initLogsScrollDetection(); @@ -391,12 +391,13 @@ document.addEventListener('DOMContentLoaded', function() { initModalAccessibility(); initPromptSourceToggle(); + // Load evil mode status FIRST so mood dropdowns populate with the correct list + await checkEvilModeStatus(); + loadStatus(); - loadServers(); - populateMoodDropdowns(); + loadServers(); // internally calls populateMoodDropdowns() with correct evilMode loadLastPrompt(); loadLogs(); - checkEvilModeStatus(); checkBipolarModeStatus(); checkGPUStatus(); refreshLanguageStatus(); diff --git a/bot/static/js/modes.js b/bot/static/js/modes.js index 4a406cb..3801ee1 100644 --- a/bot/static/js/modes.js +++ b/bot/static/js/modes.js @@ -90,6 +90,9 @@ function updateEvilModeUI() { } updateBipolarToggleVisibility(); + + // Update per-server mood controls to reflect evil mode state + populateMoodDropdowns(); } // ===== GPU Selection Management ===== diff --git a/bot/static/js/servers.js b/bot/static/js/servers.js index e4bf21b..ad01bcc 100644 --- a/bot/static/js/servers.js +++ b/bot/static/js/servers.js @@ -80,13 +80,16 @@ function displayServers() {

Server Mood

Current Mood: ${server.current_mood_name || 'neutral'} ${MOOD_EMOJIS[server.current_mood_name] || ''}
Sleeping: ${server.is_sleeping ? 'Yes' : 'No'}
-
+
+
`).join(''); @@ -375,7 +378,7 @@ async function repairConfig() { } } -// Populate mood dropdowns with available moods +// Populate mood dropdowns with available moods (Evil Mode-aware) async function populateMoodDropdowns() { try { console.log('🎭 Loading available moods...'); @@ -384,17 +387,28 @@ async function populateMoodDropdowns() { if (data.moods) { console.log(`🎭 Found ${data.moods.length} moods:`, data.moods); - const emojiMap = evilMode ? EVIL_MOOD_EMOJIS : MOOD_EMOJIS; + + // Determine which mood list to use based on evil mode + let moodList = data.moods; + let emojiMap = MOOD_EMOJIS; + let defaultMood = 'neutral'; + + if (evilMode) { + // Evil Miku uses evil moods for the global DM dropdown + moodList = Object.keys(EVIL_MOOD_EMOJIS); + emojiMap = EVIL_MOOD_EMOJIS; + defaultMood = 'evil_neutral'; + } // Populate the DM mood dropdown (#mood on tab1) const dmMoodSelect = document.getElementById('mood'); if (dmMoodSelect) { dmMoodSelect.innerHTML = ''; - data.moods.forEach(mood => { + moodList.forEach(mood => { const opt = document.createElement('option'); opt.value = mood; opt.textContent = `${emojiMap[mood] || ''} ${mood}`.trim(); - if (mood === 'neutral') opt.selected = true; + if (mood === defaultMood) opt.selected = true; dmMoodSelect.appendChild(opt); }); } @@ -403,32 +417,17 @@ async function populateMoodDropdowns() { const chatMoodSelect = document.getElementById('chat-mood-select'); if (chatMoodSelect) { chatMoodSelect.innerHTML = ''; - data.moods.forEach(mood => { + moodList.forEach(mood => { const opt = document.createElement('option'); opt.value = mood; opt.textContent = `${emojiMap[mood] || ''} ${mood}`.trim(); - if (mood === 'neutral') opt.selected = true; + if (mood === defaultMood) opt.selected = true; chatMoodSelect.appendChild(opt); }); } - // Populate per-server mood dropdowns (mood-select-{guildId}) - document.querySelectorAll('[id^="mood-select-"]').forEach(select => { - // Keep only the first option ("Select Mood...") - while (select.children.length > 1) { - select.removeChild(select.lastChild); - } - }); - - data.moods.forEach(mood => { - const moodOption = document.createElement('option'); - moodOption.value = mood; - moodOption.textContent = `${mood} ${emojiMap[mood] || ''}`; - - document.querySelectorAll('[id^="mood-select-"]').forEach(select => { - select.appendChild(moodOption.cloneNode(true)); - }); - }); + // Update per-server mood controls based on evil mode state + updatePerServerMoodControls(data.moods, MOOD_EMOJIS); console.log('🎭 All mood dropdowns populated successfully'); } else { @@ -439,6 +438,70 @@ async function populateMoodDropdowns() { } } +// Update per-server mood controls: enable/disable based on evil mode +function updatePerServerMoodControls(regularMoods, regularEmojiMap) { + const serverSelects = document.querySelectorAll('[id^="mood-select-"]'); + + if (evilMode) { + // Evil Miku is active — disable per-server mood controls + serverSelects.forEach(select => { + const guildId = select.id.replace('mood-select-', ''); + const controlsDiv = select.closest('.server-mood-controls'); + const noticeDiv = document.getElementById(`evil-notice-${guildId}`); + + // Disable the select + select.disabled = true; + + // Disable buttons in the same controls div + if (controlsDiv) { + controlsDiv.querySelectorAll('button').forEach(btn => { + btn.disabled = true; + }); + } + + // Show the explanation notice + if (noticeDiv) { + noticeDiv.style.display = 'block'; + } + }); + } else { + // Normal mode — enable per-server mood controls and populate with regular moods + serverSelects.forEach(select => { + const guildId = select.id.replace('mood-select-', ''); + const controlsDiv = select.closest('.server-mood-controls'); + const noticeDiv = document.getElementById(`evil-notice-${guildId}`); + + // Enable the select + select.disabled = false; + + // Enable buttons in the same controls div + if (controlsDiv) { + controlsDiv.querySelectorAll('button').forEach(btn => { + btn.disabled = false; + }); + } + + // Hide the explanation notice + if (noticeDiv) { + noticeDiv.style.display = 'none'; + } + + // Populate with regular moods + // Keep only the first option ("Select Mood...") + while (select.children.length > 1) { + select.removeChild(select.lastChild); + } + + regularMoods.forEach(mood => { + const moodOption = document.createElement('option'); + moodOption.value = mood; + moodOption.textContent = `${mood} ${regularEmojiMap[mood] || ''}`; + select.appendChild(moodOption); + }); + }); + } +} + // Per-Server Mood Management async function setServerMood(guildId) { console.log(`🎭 setServerMood called with guildId: ${guildId} (type: ${typeof guildId})`);