Files
miku-discord/bot/static/js/modes.js

403 lines
13 KiB
JavaScript
Raw Normal View History

refactor: Modularize monolithic HTML control panel into organized components This commit completes a major refactoring of the Miku control panel from a single 7,191-line monolithic HTML file to a modern modular architecture: CHANGES: - Extracted 872 lines of CSS into css/style.css - Created 10 specialized JavaScript modules (4,964 lines total): * core.js: Global state, utilities, initialization, polling system * servers.js: Server management and mood handling * modes.js: Evil mode, GPU selection, bipolar mode, scoreboard * actions.js: Autonomous/manual actions, custom prompts, reactions * image-gen.js: Image generation system * status.js: Status display and statistics * dm.js: DM user management and conversation analysis * chat.js: LLM chat interface with streaming and voice calls * memories.js: Cheshire Cat memory integration (episodic/declarative/procedural) * profile.js: Profile picture, album gallery, activities editor - Cleaned index.html to 1,351 lines (structure only, zero inline JS/CSS) - Removed 12 duplicate variable declarations - Maintained strict script load order for dependency resolution - Added backup comment to index.html.bak for historical reference VERIFICATION COMPLETED: ✓ All 191 functions/variables from original accounted for ✓ Cross-referenced with backup to ensure nothing lost ✓ All onclick handlers and modal systems validated ✓ No circular dependencies or broken references ✓ HTML structure integrity verified (11 tabs, all buttons/modals intact) ✓ CropperJS CDN links preserved The refactored code is production-ready with improved maintainability and clear separation of concerns.
2026-04-29 20:56:49 +03:00
// ============================================================================
// Miku Control Panel — Modes Module
// Evil Mode, GPU Selection, Bipolar Mode
// ============================================================================
// ===== Evil Mode Functions =====
async function checkEvilModeStatus() {
try {
const result = await apiCall('/evil-mode');
evilMode = result.evil_mode;
updateEvilModeUI();
if (evilMode && result.mood) {
const moodSelect = document.getElementById('mood');
moodSelect.value = result.mood;
}
} catch (error) {
console.error('Failed to check evil mode status:', error);
}
}
async function toggleEvilMode() {
try {
const toggleBtn = document.getElementById('evil-mode-toggle');
toggleBtn.disabled = true;
toggleBtn.textContent = '⏳ Switching...';
const result = await apiCall('/evil-mode/toggle', 'POST');
evilMode = result.evil_mode;
updateEvilModeUI();
if (evilMode) {
showNotification('😈 Evil Mode enabled! Evil Miku has awakened...');
} else {
showNotification('🎤 Evil Mode disabled. Normal Miku is back!');
}
} catch (error) {
console.error('Failed to toggle evil mode:', error);
showNotification('Failed to toggle evil mode: ' + error.message, 'error');
}
}
function updateEvilModeUI() {
const body = document.body;
const title = document.getElementById('panel-title');
const toggleBtn = document.getElementById('evil-mode-toggle');
const moodSelect = document.getElementById('mood');
if (evilMode) {
body.classList.add('evil-mode');
title.textContent = 'Evil Miku Control Panel';
toggleBtn.textContent = '😈 Evil Mode: ON';
toggleBtn.disabled = false;
moodSelect.innerHTML = `
<option value="aggressive">👿 aggressive</option>
<option value="bored">🥱 bored</option>
<option value="contemptuous">👑 contemptuous</option>
<option value="cunning">🐍 cunning</option>
<option value="evil_neutral" selected>evil neutral</option>
<option value="jealous">💚 jealous</option>
<option value="manic">🤪 manic</option>
<option value="melancholic">🌑 melancholic</option>
<option value="playful_cruel">🎭 playful cruel</option>
<option value="sarcastic">😈 sarcastic</option>
`;
} else {
body.classList.remove('evil-mode');
title.textContent = 'Miku Control Panel';
toggleBtn.textContent = '😈 Evil Mode: OFF';
toggleBtn.disabled = false;
moodSelect.innerHTML = `
<option value="angry">💢 angry</option>
<option value="asleep">💤 asleep</option>
<option value="bubbly">🫧 bubbly</option>
<option value="curious">👀 curious</option>
<option value="excited"> excited</option>
<option value="flirty">🫦 flirty</option>
<option value="irritated">😒 irritated</option>
<option value="melancholy">🍷 melancholy</option>
<option value="neutral" selected>neutral</option>
<option value="romantic">💌 romantic</option>
<option value="serious">👔 serious</option>
<option value="shy">👉👈 shy</option>
<option value="silly">🪿 silly</option>
<option value="sleepy">🌙 sleepy</option>
`;
}
updateBipolarToggleVisibility();
}
// ===== GPU Selection Management =====
async function checkGPUStatus() {
try {
const data = await apiCall('/gpu-status');
selectedGPU = data.gpu || 'nvidia';
updateGPUUI();
} catch (error) {
console.error('Failed to check GPU status:', error);
}
}
async function toggleGPU() {
try {
const toggleBtn = document.getElementById('gpu-selector-toggle');
toggleBtn.disabled = true;
toggleBtn.textContent = '⏳ Switching...';
const result = await apiCall('/gpu-select', 'POST', {
gpu: selectedGPU === 'nvidia' ? 'amd' : 'nvidia'
});
selectedGPU = result.gpu;
updateGPUUI();
const gpuName = selectedGPU === 'nvidia' ? 'NVIDIA GTX 1660' : 'AMD RX 6800';
showNotification(`🎮 Switched to ${gpuName}!`);
} catch (error) {
console.error('Failed to toggle GPU:', error);
showNotification('Failed to switch GPU: ' + error.message, 'error');
toggleBtn.disabled = false;
}
}
function updateGPUUI() {
const toggleBtn = document.getElementById('gpu-selector-toggle');
if (selectedGPU === 'amd') {
toggleBtn.textContent = '🎮 GPU: AMD';
toggleBtn.style.background = '#c91432';
toggleBtn.style.borderColor = '#e91436';
} else {
toggleBtn.textContent = '🎮 GPU: NVIDIA';
toggleBtn.style.background = '#2a5599';
toggleBtn.style.borderColor = '#4a7bc9';
}
toggleBtn.disabled = false;
}
// ===== Bipolar Mode Management =====
async function checkBipolarModeStatus() {
try {
const data = await apiCall('/bipolar-mode');
bipolarMode = data.bipolar_mode;
updateBipolarModeUI();
} catch (error) {
console.error('Failed to check bipolar mode status:', error);
}
}
async function toggleBipolarMode() {
try {
const toggleBtn = document.getElementById('bipolar-mode-toggle');
toggleBtn.disabled = true;
toggleBtn.textContent = '⏳ Switching...';
const result = await apiCall('/bipolar-mode/toggle', 'POST');
bipolarMode = result.bipolar_mode;
updateBipolarModeUI();
if (bipolarMode) {
showNotification('🔄 Bipolar Mode enabled! Both Mikus can now argue...');
} else {
showNotification('🔄 Bipolar Mode disabled.');
}
} catch (error) {
console.error('Failed to toggle bipolar mode:', error);
showNotification('Failed to toggle bipolar mode: ' + error.message, 'error');
}
}
function updateBipolarModeUI() {
const toggleBtn = document.getElementById('bipolar-mode-toggle');
const bipolarSection = document.getElementById('bipolar-section');
if (bipolarMode) {
toggleBtn.textContent = '🔄 Bipolar: ON';
toggleBtn.style.background = '#9932CC';
toggleBtn.style.borderColor = '#9932CC';
toggleBtn.disabled = false;
if (bipolarSection) {
bipolarSection.style.display = 'block';
loadScoreboard();
}
} else {
toggleBtn.textContent = '🔄 Bipolar: OFF';
toggleBtn.style.background = '#333';
toggleBtn.style.borderColor = '#666';
toggleBtn.disabled = false;
if (bipolarSection) {
bipolarSection.style.display = 'none';
}
}
}
function updateBipolarToggleVisibility() {
const bipolarToggle = document.getElementById('bipolar-mode-toggle');
bipolarToggle.style.display = 'block';
}
async function triggerPersonaDialogue() {
const messageIdInput = document.getElementById('dialogue-message-id').value.trim();
const statusDiv = document.getElementById('dialogue-status');
if (!messageIdInput) {
showNotification('Please enter a message ID', 'error');
return;
}
if (!/^\d+$/.test(messageIdInput)) {
showNotification('Invalid message ID format - should be a number', 'error');
return;
}
try {
statusDiv.innerHTML = '<span style="color: #6B8EFF;">⏳ Analyzing message for dialogue trigger...</span>';
const requestBody = {
message_id: messageIdInput
};
const result = await apiCall('/bipolar-mode/trigger-dialogue', 'POST', requestBody);
if (result.status === 'error') {
statusDiv.innerHTML = `<span style="color: #ff4444;">❌ ${result.message}</span>`;
showNotification(result.message, 'error');
return;
}
statusDiv.innerHTML = `<span style="color: #00ff00;">✅ ${result.message}</span>`;
showNotification(`💬 ${result.message}`);
document.getElementById('dialogue-message-id').value = '';
} catch (error) {
statusDiv.innerHTML = `<span style="color: #ff4444;">❌ Failed to trigger dialogue: ${error.message}</span>`;
showNotification(`Error: ${error.message}`, 'error');
}
}
async function triggerBipolarArgument() {
const channelIdInput = document.getElementById('bipolar-channel-id').value.trim();
const messageIdInput = document.getElementById('bipolar-message-id').value.trim();
const context = document.getElementById('bipolar-context').value;
const topic = document.getElementById('bipolar-topic').value.trim();
refactor: Modularize monolithic HTML control panel into organized components This commit completes a major refactoring of the Miku control panel from a single 7,191-line monolithic HTML file to a modern modular architecture: CHANGES: - Extracted 872 lines of CSS into css/style.css - Created 10 specialized JavaScript modules (4,964 lines total): * core.js: Global state, utilities, initialization, polling system * servers.js: Server management and mood handling * modes.js: Evil mode, GPU selection, bipolar mode, scoreboard * actions.js: Autonomous/manual actions, custom prompts, reactions * image-gen.js: Image generation system * status.js: Status display and statistics * dm.js: DM user management and conversation analysis * chat.js: LLM chat interface with streaming and voice calls * memories.js: Cheshire Cat memory integration (episodic/declarative/procedural) * profile.js: Profile picture, album gallery, activities editor - Cleaned index.html to 1,351 lines (structure only, zero inline JS/CSS) - Removed 12 duplicate variable declarations - Maintained strict script load order for dependency resolution - Added backup comment to index.html.bak for historical reference VERIFICATION COMPLETED: ✓ All 191 functions/variables from original accounted for ✓ Cross-referenced with backup to ensure nothing lost ✓ All onclick handlers and modal systems validated ✓ No circular dependencies or broken references ✓ HTML structure integrity verified (11 tabs, all buttons/modals intact) ✓ CropperJS CDN links preserved The refactored code is production-ready with improved maintainability and clear separation of concerns.
2026-04-29 20:56:49 +03:00
const statusDiv = document.getElementById('bipolar-status');
if (!channelIdInput) {
showNotification('Please enter a channel ID', 'error');
return;
}
if (!/^\d+$/.test(channelIdInput)) {
showNotification('Invalid channel ID format - should be a number', 'error');
return;
}
if (messageIdInput && !/^\d+$/.test(messageIdInput)) {
showNotification('Invalid message ID format - should be a number', 'error');
return;
}
try {
statusDiv.innerHTML = '<span style="color: #9932CC;">⏳ Triggering argument...</span>';
const requestBody = {
channel_id: channelIdInput,
context: context
};
if (messageIdInput) {
requestBody.message_id = messageIdInput;
}
if (topic) {
requestBody.topic = topic;
}
refactor: Modularize monolithic HTML control panel into organized components This commit completes a major refactoring of the Miku control panel from a single 7,191-line monolithic HTML file to a modern modular architecture: CHANGES: - Extracted 872 lines of CSS into css/style.css - Created 10 specialized JavaScript modules (4,964 lines total): * core.js: Global state, utilities, initialization, polling system * servers.js: Server management and mood handling * modes.js: Evil mode, GPU selection, bipolar mode, scoreboard * actions.js: Autonomous/manual actions, custom prompts, reactions * image-gen.js: Image generation system * status.js: Status display and statistics * dm.js: DM user management and conversation analysis * chat.js: LLM chat interface with streaming and voice calls * memories.js: Cheshire Cat memory integration (episodic/declarative/procedural) * profile.js: Profile picture, album gallery, activities editor - Cleaned index.html to 1,351 lines (structure only, zero inline JS/CSS) - Removed 12 duplicate variable declarations - Maintained strict script load order for dependency resolution - Added backup comment to index.html.bak for historical reference VERIFICATION COMPLETED: ✓ All 191 functions/variables from original accounted for ✓ Cross-referenced with backup to ensure nothing lost ✓ All onclick handlers and modal systems validated ✓ No circular dependencies or broken references ✓ HTML structure integrity verified (11 tabs, all buttons/modals intact) ✓ CropperJS CDN links preserved The refactored code is production-ready with improved maintainability and clear separation of concerns.
2026-04-29 20:56:49 +03:00
const result = await apiCall('/bipolar-mode/trigger-argument', 'POST', requestBody);
if (result.status === 'error') {
statusDiv.innerHTML = `<span style="color: #ff4444;">❌ ${result.message}</span>`;
showNotification(result.message, 'error');
return;
}
statusDiv.innerHTML = `<span style="color: #00ff00;">✅ ${result.message}</span>`;
showNotification(`⚔️ Argument triggered!`);
document.getElementById('bipolar-context').value = '';
document.getElementById('bipolar-topic').value = '';
refactor: Modularize monolithic HTML control panel into organized components This commit completes a major refactoring of the Miku control panel from a single 7,191-line monolithic HTML file to a modern modular architecture: CHANGES: - Extracted 872 lines of CSS into css/style.css - Created 10 specialized JavaScript modules (4,964 lines total): * core.js: Global state, utilities, initialization, polling system * servers.js: Server management and mood handling * modes.js: Evil mode, GPU selection, bipolar mode, scoreboard * actions.js: Autonomous/manual actions, custom prompts, reactions * image-gen.js: Image generation system * status.js: Status display and statistics * dm.js: DM user management and conversation analysis * chat.js: LLM chat interface with streaming and voice calls * memories.js: Cheshire Cat memory integration (episodic/declarative/procedural) * profile.js: Profile picture, album gallery, activities editor - Cleaned index.html to 1,351 lines (structure only, zero inline JS/CSS) - Removed 12 duplicate variable declarations - Maintained strict script load order for dependency resolution - Added backup comment to index.html.bak for historical reference VERIFICATION COMPLETED: ✓ All 191 functions/variables from original accounted for ✓ Cross-referenced with backup to ensure nothing lost ✓ All onclick handlers and modal systems validated ✓ No circular dependencies or broken references ✓ HTML structure integrity verified (11 tabs, all buttons/modals intact) ✓ CropperJS CDN links preserved The refactored code is production-ready with improved maintainability and clear separation of concerns.
2026-04-29 20:56:49 +03:00
document.getElementById('bipolar-message-id').value = '';
loadActiveArguments();
loadScoreboard();
} catch (error) {
statusDiv.innerHTML = `<span style="color: #ff4444;">❌ ${error.message}</span>`;
showNotification('Failed to trigger argument: ' + error.message, 'error');
}
}
async function loadScoreboard() {
const scoreboardContent = document.getElementById('scoreboard-content');
try {
const result = await apiCall('/bipolar-mode/scoreboard', 'GET');
if (result.status === 'error') {
scoreboardContent.innerHTML = `<p style="color: #ff4444;">Failed to load scoreboard</p>`;
return;
}
const { scoreboard } = result;
const total = scoreboard.total_arguments;
if (total === 0) {
scoreboardContent.innerHTML = `<p style="color: #888;">No arguments have been judged yet.</p>`;
return;
}
const mikuPct = total > 0 ? ((scoreboard.miku_wins / total) * 100).toFixed(1) : 0;
const evilPct = total > 0 ? ((scoreboard.evil_wins / total) * 100).toFixed(1) : 0;
let html = `
<div style="display: flex; justify-content: space-between; margin-bottom: 0.8rem;">
<div style="text-align: center; flex: 1;">
<div style="color: #86cecb; font-size: 1.2rem; font-weight: bold;">${scoreboard.miku_wins}</div>
<div style="color: #888; font-size: 0.85rem;">Hatsune Miku</div>
<div style="color: #999; font-size: 0.75rem;">${mikuPct}%</div>
</div>
<div style="align-self: center; color: #666; font-size: 1.2rem;">vs</div>
<div style="text-align: center; flex: 1;">
<div style="color: #D60004; font-size: 1.2rem; font-weight: bold;">${scoreboard.evil_wins}</div>
<div style="color: #888; font-size: 0.85rem;">Evil Miku</div>
<div style="color: #999; font-size: 0.75rem;">${evilPct}%</div>
</div>
</div>
<div style="text-align: center; color: #aaa; font-size: 0.85rem; border-top: 1px solid #333; padding-top: 0.5rem;">
Total Arguments: ${total}
</div>
`;
if (scoreboard.history && scoreboard.history.length > 0) {
html += `<div style="margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid #333;">
<div style="color: #888; font-size: 0.8rem; margin-bottom: 0.3rem;">Recent Results:</div>`;
scoreboard.history.reverse().forEach(entry => {
const winnerName = entry.winner === 'evil' ? 'Evil Miku' : 'Hatsune Miku';
const winnerColor = entry.winner === 'evil' ? '#D60004' : '#86cecb';
const date = new Date(entry.timestamp).toLocaleString();
html += `<div style="font-size: 0.75rem; color: #666; margin-bottom: 0.2rem;">
<span style="color: ${winnerColor};">🏆 ${winnerName}</span> (${entry.exchanges} exchanges) - ${date}
</div>`;
});
html += `</div>`;
}
scoreboardContent.innerHTML = html;
} catch (error) {
scoreboardContent.innerHTML = `<p style="color: #ff4444;">Error loading scoreboard</p>`;
console.error('Scoreboard error:', error);
}
}
async function loadActiveArguments() {
try {
const data = await apiCall('/bipolar-mode/arguments');
const container = document.getElementById('active-arguments');
const list = document.getElementById('active-arguments-list');
if (Object.keys(data.active_arguments).length > 0) {
container.style.display = 'block';
list.innerHTML = '';
for (const [channelId, argData] of Object.entries(data.active_arguments)) {
const div = document.createElement('div');
div.style.background = '#2a2a3e';
div.style.padding = '0.5rem';
div.style.marginBottom = '0.5rem';
div.style.borderRadius = '4px';
div.innerHTML = `
<strong>#${argData.channel_name}</strong><br>
<small>Exchanges: ${argData.exchange_count} | Speaker: ${argData.current_speaker}</small>
`;
list.appendChild(div);
}
} else {
container.style.display = 'none';
}
} catch (error) {
console.error('Failed to load active arguments:', error);
}
}