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
This commit is contained in:
2026-05-18 21:43:44 +03:00
parent 27f0659cc8
commit 7cb21a372b
4 changed files with 112 additions and 28 deletions

View File

@@ -915,3 +915,20 @@ body.evil-mode [style*="color: rgb(0, 123, 255)"] {
color: #888; color: #888;
margin-left: auto; 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;
}

View File

@@ -383,7 +383,7 @@ async function loadProfilePictureMetadata() {
// DOMContentLoaded — main initialization // DOMContentLoaded — main initialization
// ============================================================================ // ============================================================================
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', async function() {
initTabState(); initTabState();
initTabWheelScroll(); initTabWheelScroll();
initLogsScrollDetection(); initLogsScrollDetection();
@@ -391,12 +391,13 @@ document.addEventListener('DOMContentLoaded', function() {
initModalAccessibility(); initModalAccessibility();
initPromptSourceToggle(); initPromptSourceToggle();
// Load evil mode status FIRST so mood dropdowns populate with the correct list
await checkEvilModeStatus();
loadStatus(); loadStatus();
loadServers(); loadServers(); // internally calls populateMoodDropdowns() with correct evilMode
populateMoodDropdowns();
loadLastPrompt(); loadLastPrompt();
loadLogs(); loadLogs();
checkEvilModeStatus();
checkBipolarModeStatus(); checkBipolarModeStatus();
checkGPUStatus(); checkGPUStatus();
refreshLanguageStatus(); refreshLanguageStatus();

View File

@@ -90,6 +90,9 @@ function updateEvilModeUI() {
} }
updateBipolarToggleVisibility(); updateBipolarToggleVisibility();
// Update per-server mood controls to reflect evil mode state
populateMoodDropdowns();
} }
// ===== GPU Selection Management ===== // ===== GPU Selection Management =====

View File

@@ -80,13 +80,16 @@ function displayServers() {
<h4 style="margin: 0 0 0.5rem 0; color: #61dafb;">Server Mood</h4> <h4 style="margin: 0 0 0.5rem 0; color: #61dafb;">Server Mood</h4>
<div><strong>Current Mood:</strong> ${server.current_mood_name || 'neutral'} ${MOOD_EMOJIS[server.current_mood_name] || ''}</div> <div><strong>Current Mood:</strong> ${server.current_mood_name || 'neutral'} ${MOOD_EMOJIS[server.current_mood_name] || ''}</div>
<div><strong>Sleeping:</strong> ${server.is_sleeping ? 'Yes' : 'No'}</div> <div><strong>Sleeping:</strong> ${server.is_sleeping ? 'Yes' : 'No'}</div>
<div style="margin-top: 0.5rem;"> <div class="server-mood-controls" style="margin-top: 0.5rem;">
<select id="mood-select-${String(server.guild_id)}" style="margin-right: 0.5rem; padding: 0.3rem; background: #333; color: white; border: 1px solid #555; border-radius: 3px;"> <select id="mood-select-${String(server.guild_id)}" style="margin-right: 0.5rem; padding: 0.3rem; background: #333; color: white; border: 1px solid #555; border-radius: 3px;">
<option value="">Select Mood...</option> <option value="">Select Mood...</option>
</select> </select>
<button onclick="setServerMood('${String(server.guild_id)}')" style="margin-right: 0.5rem;">Change Mood</button> <button onclick="setServerMood('${String(server.guild_id)}')" style="margin-right: 0.5rem;">Change Mood</button>
<button onclick="resetServerMood('${String(server.guild_id)}')" style="background: #ff9800;">Reset Mood</button> <button onclick="resetServerMood('${String(server.guild_id)}')" style="background: #ff9800;">Reset Mood</button>
</div> </div>
<div class="evil-mode-exempt-notice" id="evil-notice-${String(server.guild_id)}" style="display: none;">
⚠️ Per-server moods are unavailable while Evil Miku is active — she applies her global mood everywhere.
</div>
</div> </div>
</div> </div>
`).join(''); `).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() { async function populateMoodDropdowns() {
try { try {
console.log('🎭 Loading available moods...'); console.log('🎭 Loading available moods...');
@@ -384,17 +387,28 @@ async function populateMoodDropdowns() {
if (data.moods) { if (data.moods) {
console.log(`🎭 Found ${data.moods.length} moods:`, 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) // Populate the DM mood dropdown (#mood on tab1)
const dmMoodSelect = document.getElementById('mood'); const dmMoodSelect = document.getElementById('mood');
if (dmMoodSelect) { if (dmMoodSelect) {
dmMoodSelect.innerHTML = ''; dmMoodSelect.innerHTML = '';
data.moods.forEach(mood => { moodList.forEach(mood => {
const opt = document.createElement('option'); const opt = document.createElement('option');
opt.value = mood; opt.value = mood;
opt.textContent = `${emojiMap[mood] || ''} ${mood}`.trim(); opt.textContent = `${emojiMap[mood] || ''} ${mood}`.trim();
if (mood === 'neutral') opt.selected = true; if (mood === defaultMood) opt.selected = true;
dmMoodSelect.appendChild(opt); dmMoodSelect.appendChild(opt);
}); });
} }
@@ -403,32 +417,17 @@ async function populateMoodDropdowns() {
const chatMoodSelect = document.getElementById('chat-mood-select'); const chatMoodSelect = document.getElementById('chat-mood-select');
if (chatMoodSelect) { if (chatMoodSelect) {
chatMoodSelect.innerHTML = ''; chatMoodSelect.innerHTML = '';
data.moods.forEach(mood => { moodList.forEach(mood => {
const opt = document.createElement('option'); const opt = document.createElement('option');
opt.value = mood; opt.value = mood;
opt.textContent = `${emojiMap[mood] || ''} ${mood}`.trim(); opt.textContent = `${emojiMap[mood] || ''} ${mood}`.trim();
if (mood === 'neutral') opt.selected = true; if (mood === defaultMood) opt.selected = true;
chatMoodSelect.appendChild(opt); chatMoodSelect.appendChild(opt);
}); });
} }
// Populate per-server mood dropdowns (mood-select-{guildId}) // Update per-server mood controls based on evil mode state
document.querySelectorAll('[id^="mood-select-"]').forEach(select => { updatePerServerMoodControls(data.moods, MOOD_EMOJIS);
// 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));
});
});
console.log('🎭 All mood dropdowns populated successfully'); console.log('🎭 All mood dropdowns populated successfully');
} else { } 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 // Per-Server Mood Management
async function setServerMood(guildId) { async function setServerMood(guildId) {
console.log(`🎭 setServerMood called with guildId: ${guildId} (type: ${typeof guildId})`); console.log(`🎭 setServerMood called with guildId: ${guildId} (type: ${typeof guildId})`);