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'}
-
+
+
+ ⚠️ Per-server moods are unavailable while Evil Miku is active — she applies her global mood everywhere.
+
`).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})`);