- Added ?v=20260502 query param to all <script src=...> and <link> tags - Added Cache-Control: no-cache, no-store, must-revalidate to index route - Added <meta> cache-control tags in HTML head for extra coverage - This ensures the browser always fetches fresh HTML/JS/CSS after deploy, preventing the old loadLastPrompt() from running against new HTML (which would crash since #prompt-cat-info no longer exists)
1387 lines
77 KiB
HTML
1387 lines
77 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta http-equiv="Pragma" content="no-cache">
|
||
<meta http-equiv="Expires" content="0">
|
||
<title>Miku Control Panel</title>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.css">
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.js"></script>
|
||
<link rel="stylesheet" href="/static/css/style.css?v=20260502">
|
||
</head>
|
||
<body>
|
||
|
||
<div class="panel">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
||
<div style="display: flex; gap: 1rem; align-items: center;">
|
||
<h1 id="panel-title">Miku Control Panel</h1>
|
||
<button id="gpu-selector-toggle" onclick="toggleGPU()" style="background: #2a5599; color: #fff; padding: 0.5rem 1rem; border: 2px solid #4a7bc9; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.9rem;">
|
||
🎮 GPU: NVIDIA
|
||
</button>
|
||
</div>
|
||
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||
<button id="bipolar-mode-toggle" onclick="toggleBipolarMode()" style="background: #333; color: #fff; padding: 0.5rem 1rem; border: 2px solid #666; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||
🔄 Bipolar: OFF
|
||
</button>
|
||
<button id="evil-mode-toggle" onclick="toggleEvilMode()" style="background: #333; color: #fff; padding: 0.5rem 1rem; border: 2px solid #666; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||
😈 Evil Mode: OFF
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<p style="color: #ccc; margin-bottom: 2rem;">
|
||
💬 <strong>DM Support:</strong> Users can message Miku directly in DMs. She responds to every message using the DM mood (auto-rotating every 2 hours).
|
||
</p>
|
||
|
||
<!-- Tab Navigation -->
|
||
<div class="tab-container">
|
||
<div class="tab-buttons">
|
||
<button class="tab-button active" data-tab="tab1" onclick="switchTab('tab1')">Server Management</button>
|
||
<button class="tab-button" data-tab="tab2" onclick="switchTab('tab2')">Actions</button>
|
||
<button class="tab-button" data-tab="tab3" onclick="switchTab('tab3')">Status</button>
|
||
<button class="tab-button" data-tab="tab10" onclick="switchTab('tab10')">📱 DM Management</button>
|
||
<button class="tab-button" data-tab="tab4" onclick="switchTab('tab4')">⚙️ LLM Settings</button>
|
||
<button class="tab-button" data-tab="tab5" onclick="switchTab('tab5')">🎨 Image Generation</button>
|
||
<button class="tab-button" data-tab="tab6" onclick="switchTab('tab6')">📊 Autonomous Stats</button>
|
||
<button class="tab-button" data-tab="tab7" onclick="switchTab('tab7')">💬 Chat with LLM</button>
|
||
<button class="tab-button" data-tab="tab8" onclick="switchTab('tab8')">📞 Voice Call</button>
|
||
<button class="tab-button" data-tab="tab9" onclick="switchTab('tab9')">🧠 Memories</button>
|
||
<button class="tab-button" data-tab="tab11" onclick="switchTab('tab11')">🖼️ Profile Picture</button>
|
||
<button class="tab-button" onclick="window.location.href='/static/system.html'">🎛️ System Settings</button>
|
||
</div>
|
||
|
||
<!-- Tab 1 Content -->
|
||
<div id="tab1" class="tab-content active">
|
||
<div class="section">
|
||
<label for="mood">Mood:</label>
|
||
<select id="mood">
|
||
<option value="">Loading moods...</option>
|
||
</select>
|
||
<button onclick="setMood()">Set Mood</button>
|
||
<button onclick="resetMood()">Reset Mood</button>
|
||
<button onclick="calmMiku()">Calm</button>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h3>Server Management</h3>
|
||
<div id="servers-list"></div>
|
||
|
||
<div class="add-server-form">
|
||
<h4>Add New Server</h4>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Guild ID:</label>
|
||
<input type="number" id="new-guild-id" placeholder="Discord Server ID">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Server Name:</label>
|
||
<input type="text" id="new-guild-name" placeholder="Server Name">
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Autonomous Channel ID:</label>
|
||
<input type="number" id="new-autonomous-channel-id" placeholder="Channel ID">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Channel Name:</label>
|
||
<input type="text" id="new-autonomous-channel-name" placeholder="Channel Name">
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Bedtime Channel IDs (comma-separated):</label>
|
||
<input type="text" id="new-bedtime-channel-ids" placeholder="Channel IDs">
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label>Enabled Features:</label>
|
||
<div class="checkbox-group">
|
||
<div class="checkbox-item">
|
||
<input type="checkbox" id="feature-autonomous" checked>
|
||
<label for="feature-autonomous">Autonomous</label>
|
||
</div>
|
||
<div class="checkbox-item">
|
||
<input type="checkbox" id="feature-bedtime" checked>
|
||
<label for="feature-bedtime">Bedtime</label>
|
||
</div>
|
||
<div class="checkbox-item">
|
||
<input type="checkbox" id="feature-monday-video" checked>
|
||
<label for="feature-monday-video">Monday Video</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button onclick="addServer()">Add Server</button>
|
||
</div>
|
||
|
||
<div style="margin-top: 1rem;">
|
||
<button onclick="repairConfig()" style="background: #ff9800;">🔧 Repair Configuration</button>
|
||
<p style="font-size: 0.9rem; color: #ccc; margin-top: 0.5rem;">
|
||
Use this if you're seeing incorrect server IDs or other configuration issues
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
<!-- Actions Tab Content -->
|
||
<div id="tab2" class="tab-content">
|
||
<div class="section">
|
||
<h3>Autonomous Actions</h3>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="server-select">Target Server:</label>
|
||
<select id="server-select">
|
||
<option value="all">All Servers</option>
|
||
</select>
|
||
</div>
|
||
<button onclick="triggerAutonomous('general')">Say Something General</button>
|
||
|
||
<!-- Engage User Submenu -->
|
||
<div style="margin-bottom: 1rem;">
|
||
<button onclick="toggleEngageSubmenu()">Engage User ▼</button>
|
||
<div id="engage-submenu" style="display: none; margin-left: 1rem; margin-top: 0.5rem; padding: 1rem; background: #1e1e1e; border: 1px solid #444; border-radius: 4px;">
|
||
<div style="margin-bottom: 0.5rem;">
|
||
<label for="engage-user-id" style="display: block; margin-bottom: 0.3rem;">User ID (leave empty for random):</label>
|
||
<input type="text" id="engage-user-id" placeholder="User ID" style="width: 200px;">
|
||
</div>
|
||
|
||
<div style="margin-bottom: 0.5rem;">
|
||
<label style="display: block; margin-bottom: 0.3rem;">Engagement Type:</label>
|
||
<div style="margin-left: 0.5rem;">
|
||
<label style="display: block; margin-bottom: 0.2rem;">
|
||
<input type="radio" name="engage-type" value="random" checked> Random (auto-detect)
|
||
</label>
|
||
<label style="display: block; margin-bottom: 0.2rem;">
|
||
<input type="radio" name="engage-type" value="activity"> Activity-based (comment on what they're doing)
|
||
</label>
|
||
<label style="display: block; margin-bottom: 0.2rem;">
|
||
<input type="radio" name="engage-type" value="general"> General conversation
|
||
</label>
|
||
<label style="display: block; margin-bottom: 0.2rem;">
|
||
<input type="radio" name="engage-type" value="status"> Status-based (online/idle/invisible)
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<button onclick="triggerEngageUser()">🚀 Engage User</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Share Tweet Submenu -->
|
||
<div style="margin-bottom: 1rem;">
|
||
<button onclick="toggleTweetSubmenu()">Share Tweet ▼</button>
|
||
<div id="tweet-submenu" style="display: none; margin-left: 1rem; margin-top: 0.5rem; padding: 1rem; background: #1e1e1e; border: 1px solid #444; border-radius: 4px;">
|
||
<div style="margin-bottom: 0.5rem;">
|
||
<label for="tweet-url" style="display: block; margin-bottom: 0.3rem;">Tweet URL (leave empty for auto-fetch):</label>
|
||
<input type="text" id="tweet-url" placeholder="https://x.com/... or https://twitter.com/... or https://fxtwitter.com/..." style="width: 100%;">
|
||
</div>
|
||
<button onclick="triggerShareTweet()">🐦 Share Tweet</button>
|
||
</div>
|
||
</div>
|
||
<button onclick="triggerAutonomous('reaction')">React to Message</button>
|
||
<button onclick="triggerAutonomous('join-conversation')">Detect and Join Conversation</button>
|
||
<button onclick="toggleCustomPrompt()">Custom Prompt</button>
|
||
</div>
|
||
|
||
<!-- Bipolar Mode Section (only visible when bipolar mode is on) -->
|
||
<div id="bipolar-section" class="section" style="display: none; border: 2px solid #9932CC; padding: 1rem; border-radius: 8px; background: #1a1a2e;">
|
||
<h3 style="color: #9932CC;">🔄 Bipolar Mode Controls</h3>
|
||
<p style="font-size: 0.9rem; color: #aaa;">Trigger arguments or dialogues between Regular Miku and Evil Miku</p>
|
||
|
||
<!-- Persona Dialogue Section -->
|
||
<div style="margin-bottom: 2rem; padding: 1rem; background: #252540; border-radius: 8px; border: 1px solid #555;">
|
||
<h4 style="color: #6B8EFF; margin-bottom: 0.5rem;">💬 Trigger Persona Dialogue</h4>
|
||
<p style="font-size: 0.85rem; color: #999; margin-bottom: 1rem;">Start a natural conversation between the personas (can escalate to argument if tension builds)</p>
|
||
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="dialogue-message-id">Message ID:</label>
|
||
<input type="text" id="dialogue-message-id" placeholder="e.g., 1234567890123456789" style="width: 250px; margin-left: 0.5rem; font-family: monospace;">
|
||
</div>
|
||
|
||
<div style="font-size: 0.8rem; color: #888; margin-bottom: 1rem;">
|
||
💡 <strong>Tip:</strong> Right-click any bot response message in Discord and select "Copy Message ID". The opposite persona will analyze it and decide whether to interject.
|
||
</div>
|
||
|
||
<button onclick="triggerPersonaDialogue()" style="background: #6B8EFF; color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer;">
|
||
💬 Trigger Dialogue
|
||
</button>
|
||
|
||
<div id="dialogue-status" style="margin-top: 1rem; font-size: 0.9rem;"></div>
|
||
</div>
|
||
|
||
<!-- Argument Section -->
|
||
<div style="padding: 1rem; background: #2e1a2e; border-radius: 8px; border: 1px solid #555;">
|
||
<h4 style="color: #9932CC; margin-bottom: 0.5rem;">⚔️ Trigger Argument</h4>
|
||
<p style="font-size: 0.85rem; color: #999; margin-bottom: 1rem;">Force an immediate argument (bypasses dialogue system)</p>
|
||
|
||
<div style="margin-bottom: 1rem; display: flex; gap: 1rem; flex-wrap: wrap;">
|
||
<div>
|
||
<label for="bipolar-channel-id">Channel ID:</label>
|
||
<input type="text" id="bipolar-channel-id" placeholder="e.g., 1234567890123456789" style="width: 200px; margin-left: 0.5rem; font-family: monospace;">
|
||
</div>
|
||
<div>
|
||
<label for="bipolar-message-id">Starting Message ID (optional):</label>
|
||
<input type="text" id="bipolar-message-id" placeholder="e.g., 1234567890123456789" style="width: 200px; margin-left: 0.5rem; font-family: monospace;">
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-size: 0.8rem; color: #888; margin-bottom: 1rem;">
|
||
💡 <strong>Tip:</strong> Right-click a message in Discord and select "Copy Message ID" (enable Developer Mode in Discord settings).
|
||
If a starting message ID is provided, the opposite persona will respond to that message.
|
||
</div>
|
||
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="bipolar-context">What should they argue about? (optional):</label>
|
||
<input type="text" id="bipolar-context" placeholder="e.g., Who's the real Miku? Whether kindness is weakness. A petty grudge..." style="width: 100%; margin-top: 0.3rem;">
|
||
<div style="font-size: 0.75rem; color: #777; margin-top: 0.2rem;">
|
||
Leave blank for a random topic. Write anything to set the argument's theme.
|
||
</div>
|
||
</div>
|
||
|
||
<button onclick="triggerBipolarArgument()" style="background: #9932CC; color: #fff; border: none; padding: 0.5rem 1rem; border-radius: 4px; cursor: pointer;">
|
||
⚔️ Trigger Argument
|
||
</button>
|
||
|
||
<div id="bipolar-status" style="margin-top: 1rem; font-size: 0.9rem;"></div>
|
||
</div>
|
||
|
||
<!-- Scoreboard Display -->
|
||
<div id="bipolar-scoreboard" style="margin-top: 1.5rem; padding: 1rem; background: #0f0f1e; border-radius: 8px; border: 1px solid #444;">
|
||
<h4 style="color: #9932CC; margin-bottom: 0.5rem;">🏆 Argument Scoreboard</h4>
|
||
<div id="scoreboard-content" style="font-size: 0.9rem;">
|
||
<p style="color: #888;">Loading scoreboard...</p>
|
||
</div>
|
||
<button onclick="loadScoreboard()" style="margin-top: 0.5rem; background: #444; color: #fff; border: none; padding: 0.3rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.85rem;">
|
||
🔄 Refresh
|
||
</button>
|
||
</div>
|
||
|
||
<div id="active-arguments" style="margin-top: 1rem; display: none;">
|
||
<h4>Active Arguments:</h4>
|
||
<div id="active-arguments-list"></div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
<div class="section">
|
||
<h3>Figurine DM Subscribers</h3>
|
||
|
||
<!-- Subscriber Management -->
|
||
<div style="margin-bottom: 1rem;">
|
||
<h4>Subscriber Management</h4>
|
||
<div style="margin-bottom: 0.5rem;">
|
||
<button onclick="refreshFigurineSubscribers()">🔄 Refresh</button>
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: end; margin-bottom: 0.5rem;">
|
||
<div>
|
||
<label for="figurine-user-id">User ID:</label>
|
||
<input type="text" id="figurine-user-id" placeholder="Discord User ID (as string)" />
|
||
</div>
|
||
<button onclick="addFigurineSubscriber()">➕ Add Subscriber</button>
|
||
</div>
|
||
<div id="figurine-subscribers-list"></div>
|
||
</div>
|
||
|
||
<!-- Send to All Subscribers -->
|
||
<div style="margin-bottom: 1rem; border-top: 1px solid #444; padding-top: 1rem;">
|
||
<h4>Send to All Subscribers</h4>
|
||
<div style="margin-bottom: 0.5rem;">
|
||
<label for="figurine-tweet-url-all">Tweet URL (optional):</label>
|
||
<input type="text" id="figurine-tweet-url-all" placeholder="https://twitter.com/username/status/..." style="width: 300px;" />
|
||
</div>
|
||
<button onclick="sendFigurineNowToAll()">📨 Send to All Subscribers</button>
|
||
<div id="figurine-all-status" style="margin-top: 0.5rem; font-size: 0.9rem;"></div>
|
||
</div>
|
||
|
||
<!-- Send to Single User -->
|
||
<div style="border-top: 1px solid #444; padding-top: 1rem;">
|
||
<h4>Send to Single User</h4>
|
||
<div style="display: flex; gap: 10px; align-items: end; margin-bottom: 0.5rem;">
|
||
<div>
|
||
<label for="figurine-single-user-id">User ID:</label>
|
||
<input type="text" id="figurine-single-user-id" placeholder="Discord User ID" />
|
||
</div>
|
||
<div>
|
||
<label for="figurine-tweet-url-single">Tweet URL (optional):</label>
|
||
<input type="text" id="figurine-tweet-url-single" placeholder="https://twitter.com/username/status/..." style="width: 250px;" />
|
||
</div>
|
||
<button onclick="sendFigurineToSingleUser()">📨 Send to User</button>
|
||
</div>
|
||
<div id="figurine-single-status" style="margin-top: 0.5rem; font-size: 0.9rem;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h3>Manual Actions</h3>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="manual-server-select">Target Server:</label>
|
||
<select id="manual-server-select">
|
||
<option value="all">All Servers</option>
|
||
</select>
|
||
</div>
|
||
<button onclick="forceSleep()">Force Sleep</button>
|
||
<button onclick="wakeUp()">Wake Up</button>
|
||
<button onclick="sendBedtime()">Send Bedtime</button>
|
||
<button onclick="resetConversation()">Reset Conversation</button>
|
||
</div>
|
||
|
||
<div class="section" id="custom-prompt-section">
|
||
<h3>🎙️ Send Custom Prompt to Miku</h3>
|
||
|
||
<!-- Target Selection -->
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="custom-prompt-target-type">Target Type:</label>
|
||
<select id="custom-prompt-target-type" onchange="toggleCustomPromptTarget()" style="margin-right: 1rem;">
|
||
<option value="server">Server</option>
|
||
<option value="dm">Direct Message</option>
|
||
</select>
|
||
|
||
<!-- Server Selection -->
|
||
<span id="custom-prompt-server-section">
|
||
<label for="custom-prompt-server-select">Target Server:</label>
|
||
<select id="custom-prompt-server-select">
|
||
<option value="all">All Servers</option>
|
||
</select>
|
||
</span>
|
||
|
||
<!-- DM User ID Input -->
|
||
<span id="custom-prompt-dm-section" style="display: none;">
|
||
<label for="custom-prompt-user-id">User ID:</label>
|
||
<input type="text" id="custom-prompt-user-id" placeholder="Discord User ID" style="width: 200px;" />
|
||
</span>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="customPrompt">Custom Prompt:</label>
|
||
<textarea id="customPrompt" placeholder="e.g. Talk about how nice the weather is today" rows="3" style="width: 100%; margin-top: 0.5rem;"></textarea>
|
||
</div>
|
||
<div style="margin-top: 0.5rem;">
|
||
<label for="customPromptAttachment">Attach File (optional):</label>
|
||
<input type="file" id="customPromptAttachment" multiple />
|
||
</div>
|
||
<button onclick="sendCustomPrompt()" style="margin-top: 0.5rem;">Send Custom Prompt</button>
|
||
<p id="customStatus" style="color: green; margin-top: 0.5rem;"></p>
|
||
</div>
|
||
|
||
<div class="section" id="manual-message-section">
|
||
<h3>🎭 Send Message as Miku (Manual Override)</h3>
|
||
|
||
<!-- Webhook Option -->
|
||
<div style="margin-bottom: 1rem; padding: 0.5rem; background: #2a2a2a; border-radius: 4px;">
|
||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||
<input type="checkbox" id="manual-use-webhook" onchange="toggleWebhookOptions()" style="margin-right: 0.5rem;" />
|
||
<span>Send as Webhook (allows choosing persona)</span>
|
||
</label>
|
||
|
||
<div id="webhook-persona-options" style="display: none; margin-top: 0.5rem; padding-left: 1.5rem;">
|
||
<label style="display: block; margin-bottom: 0.3rem;">
|
||
<input type="radio" name="webhook-persona" value="miku" checked style="margin-right: 0.5rem;" />
|
||
Hatsune Miku 💙 (with mood emoji)
|
||
</label>
|
||
<label style="display: block;">
|
||
<input type="radio" name="webhook-persona" value="evil" style="margin-right: 0.5rem;" />
|
||
Evil Miku 😈 (with mood emoji)
|
||
</label>
|
||
<p style="font-size: 0.8rem; color: #888; margin: 0.3rem 0 0 0;">
|
||
Note: Webhooks only work in channels, not DMs. Profile picture and mood emoji will be used.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Target Selection -->
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="manual-target-type">Target Type:</label>
|
||
<select id="manual-target-type" onchange="toggleManualMessageTarget()">
|
||
<option value="channel">Channel</option>
|
||
<option value="dm">Direct Message</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="manualMessage">Message:</label>
|
||
<textarea id="manualMessage" placeholder="Type the message exactly as Miku should say it..." rows="3" style="width: 100%; margin-top: 0.5rem;"></textarea>
|
||
</div>
|
||
<div style="margin-top: 0.5rem;">
|
||
<label for="manualAttachment">Attach Files (optional):</label>
|
||
<input type="file" id="manualAttachment" multiple />
|
||
</div>
|
||
|
||
<!-- Channel ID Input -->
|
||
<div id="manual-channel-section" style="margin-top: 0.5rem;">
|
||
<label for="manualChannelId">Channel ID:</label>
|
||
<input type="text" id="manualChannelId" placeholder="Enter channel ID..." style="width: 100%;" />
|
||
</div>
|
||
|
||
<!-- User ID Input -->
|
||
<div id="manual-dm-section" style="margin-top: 0.5rem; display: none;">
|
||
<label for="manualUserId">User ID:</label>
|
||
<input type="text" id="manualUserId" placeholder="Enter user ID for DM..." style="width: 100%;" />
|
||
</div>
|
||
|
||
<!-- Reply Configuration -->
|
||
<div style="margin-top: 0.5rem;">
|
||
<label for="manualReplyMessageId">Reply to Message ID (optional):</label>
|
||
<input type="text" id="manualReplyMessageId" placeholder="Enter message ID to reply to..." style="width: 100%;" />
|
||
</div>
|
||
|
||
<div style="margin-top: 0.5rem;">
|
||
<label style="margin-right: 1rem;">Mention user in reply:</label>
|
||
<label>
|
||
<input type="radio" name="manualReplyMention" value="true" checked />
|
||
Yes (ping user)
|
||
</label>
|
||
<label style="margin-left: 1rem;">
|
||
<input type="radio" name="manualReplyMention" value="false" />
|
||
No (silent reply)
|
||
</label>
|
||
</div>
|
||
|
||
<button onclick="sendManualMessage()" style="margin-top: 0.5rem;">Send as Miku</button>
|
||
<p id="manualStatus" style="color: green; margin-top: 0.5rem;"></p>
|
||
</div>
|
||
|
||
<div class="section" id="message-reaction-section">
|
||
<h3>😊 Add Reaction to Message</h3>
|
||
<p style="color: #ccc; margin-bottom: 1rem;">
|
||
Make Miku react to a specific message with an emoji of your choice.
|
||
</p>
|
||
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="reactionMessageId">Message ID:</label>
|
||
<input type="text" id="reactionMessageId" placeholder="Enter message ID (right-click message > Copy ID)" style="width: 100%; margin-top: 0.5rem;" />
|
||
</div>
|
||
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="reactionChannelId">Channel ID:</label>
|
||
<input type="text" id="reactionChannelId" placeholder="Enter channel ID (right-click channel > Copy ID)" style="width: 100%; margin-top: 0.5rem;" />
|
||
</div>
|
||
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="reactionEmoji">Emoji:</label>
|
||
<input type="text" id="reactionEmoji" placeholder="Enter emoji (e.g., 💙, 👍, 🎉)" style="width: 100%; margin-top: 0.5rem;" />
|
||
<p style="font-size: 0.85rem; color: #aaa; margin-top: 0.25rem;">
|
||
You can use standard emoji or custom server emoji format (:emoji_name: for custom ones)
|
||
</p>
|
||
</div>
|
||
|
||
<button onclick="addReactionToMessage()" style="margin-top: 0.5rem;">Add Reaction</button>
|
||
<p id="reactionStatus" style="color: green; margin-top: 0.5rem;"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Status Tab Content -->
|
||
<div id="tab3" class="tab-content">
|
||
<div class="section">
|
||
<h3>Status</h3>
|
||
<div id="status"></div>
|
||
</div>
|
||
|
||
<!-- Mood Activities Section (collapsible) -->
|
||
<div class="section">
|
||
<div style="cursor: pointer; user-select: none;" onclick="activitiesToggle()">
|
||
<h3 style="display: inline;"><span id="activities-toggle-icon">▶</span> 🎵 Mood Activities</h3>
|
||
<span id="activities-summary" style="color: #888; font-size: 0.85rem; margin-left: 0.5rem;"></span>
|
||
</div>
|
||
<div id="activities-body" style="display: none; margin-top: 1rem;">
|
||
<div style="margin-bottom: 1rem; display: flex; gap: 0.5rem; align-items: center;">
|
||
<button onclick="activitiesLoad()" style="background: #4a7bc9;">🔄 Reload from Disk</button>
|
||
<span id="activities-status" style="font-size: 0.85rem; color: #61dafb;"></span>
|
||
</div>
|
||
|
||
<!-- Current Activity Override Panel -->
|
||
<div style="margin-bottom: 1rem; padding: 0.75rem; background: #252535; border-radius: 6px; border: 1px solid #3a3a5a;">
|
||
<h4 style="margin: 0 0 0.5rem 0; color: #61dafb; font-size: 0.95rem;">🎯 Current Activity</h4>
|
||
<div id="activity-override-status" style="margin-bottom: 0.5rem; font-size: 0.85rem; color: #aaa;">
|
||
Loading...
|
||
</div>
|
||
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.5rem;">
|
||
<button onclick="activityRefreshCurrent()" style="background: #555; font-size: 0.8rem; padding: 0.3rem 0.6rem;">🔄 Refresh</button>
|
||
<button onclick="activityReleaseAuto()" style="background: #27ae60; font-size: 0.8rem; padding: 0.3rem 0.6rem;">🌀 Return to Auto</button>
|
||
</div>
|
||
<details style="margin-top: 0.5rem;">
|
||
<summary style="cursor: pointer; font-size: 0.85rem; color: #61dafb;">✏️ Manual Override</summary>
|
||
<div style="margin-top: 0.5rem; display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center;">
|
||
<select id="act-manual-type" style="width: 120px; padding: 0.3rem;">
|
||
<option value="listening">🎵 Listening</option>
|
||
<option value="playing">🎮 Playing</option>
|
||
<option value="watching">📺 Watching</option>
|
||
<option value="competing">🏆 Competing</option>
|
||
<option value="streaming">🔴 Streaming</option>
|
||
</select>
|
||
<input type="text" id="act-manual-name" placeholder="Activity name" style="flex: 2; min-width: 120px; padding: 0.3rem;">
|
||
<input type="text" id="act-manual-state" placeholder="Detail (optional)" style="flex: 1; min-width: 80px; padding: 0.3rem;">
|
||
<input type="text" id="act-manual-url" placeholder="URL (streaming)" style="flex: 1; min-width: 80px; padding: 0.3rem; display: none;">
|
||
<button onclick="activitySetManual()" style="background: #e67e22; font-size: 0.8rem; padding: 0.3rem 0.8rem;">Set</button>
|
||
<button onclick="activityClearManual()" style="background: #c0392b; font-size: 0.8rem; padding: 0.3rem 0.8rem;">Clear</button>
|
||
</div>
|
||
</details>
|
||
</div>
|
||
|
||
<!-- Normal Moods subsection -->
|
||
<div style="margin-bottom: 1.5rem;">
|
||
<div style="cursor: pointer; user-select: none; padding: 0.5rem; background: #2a2a2a; border-radius: 4px; margin-bottom: 0.5rem;" onclick="activitiesSectionToggle('normal')">
|
||
<strong><span id="activities-normal-icon">▶</span> 😇 Normal Moods</strong>
|
||
</div>
|
||
<div id="activities-normal-body" style="display: none; padding-left: 0.5rem;">
|
||
<div id="activities-normal-list"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Evil Moods subsection -->
|
||
<div style="margin-bottom: 1.5rem;">
|
||
<div style="cursor: pointer; user-select: none; padding: 0.5rem; background: #2a2a2a; border-radius: 4px; margin-bottom: 0.5rem;" onclick="activitiesSectionToggle('evil')">
|
||
<strong><span id="activities-evil-icon">▶</span> 😈 Evil Moods</strong>
|
||
</div>
|
||
<div id="activities-evil-body" style="display: none; padding-left: 0.5rem;">
|
||
<div id="activities-evil-list"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section" id="prompt-history-section">
|
||
<div class="prompt-history-header" style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">
|
||
<h3 style="margin: 0; cursor: pointer;" onclick="togglePromptHistoryCollapse()" id="prompt-history-toggle">
|
||
▼ Prompt History
|
||
</h3>
|
||
<button onclick="loadPromptHistory()" title="Refresh" style="background: none; border: 1px solid #444; color: #aaa; cursor: pointer; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.85rem;">🔄</button>
|
||
</div>
|
||
<div id="prompt-history-body">
|
||
<!-- Source filter + history selector row -->
|
||
<div style="margin-bottom: 0.75rem; display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;">
|
||
<label style="font-size: 0.9rem; color: #aaa;">Source:</label>
|
||
<div style="display: inline-flex; border-radius: 6px; overflow: hidden; border: 1px solid #444;">
|
||
<button id="prompt-src-all" class="prompt-source-btn active" onclick="switchPromptSource('all')"
|
||
style="padding: 0.4rem 0.8rem; border: none; cursor: pointer; font-size: 0.85rem; transition: all 0.2s;">
|
||
All
|
||
</button>
|
||
<button id="prompt-src-cat" class="prompt-source-btn" onclick="switchPromptSource('cat')"
|
||
style="padding: 0.4rem 0.8rem; border: none; cursor: pointer; font-size: 0.85rem; transition: all 0.2s;">
|
||
🐱 Cat
|
||
</button>
|
||
<button id="prompt-src-fallback" class="prompt-source-btn" onclick="switchPromptSource('fallback')"
|
||
style="padding: 0.4rem 0.8rem; border: none; cursor: pointer; font-size: 0.85rem; transition: all 0.2s;">
|
||
🤖 Fallback
|
||
</button>
|
||
</div>
|
||
<select id="prompt-history-select" onchange="selectPromptEntry(this.value)" style="background: #2a2a2a; color: #ddd; border: 1px solid #444; padding: 0.35rem 0.5rem; border-radius: 4px; font-size: 0.85rem; min-width: 280px;">
|
||
<option value="">-- No prompts yet --</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- Metadata bar -->
|
||
<div id="prompt-metadata" style="margin-bottom: 0.5rem; font-size: 0.82rem; color: #888; display: flex; flex-wrap: wrap; gap: 0.3rem 1rem;"></div>
|
||
|
||
<!-- Toolbar: copy + truncate toggle -->
|
||
<div style="margin-bottom: 0.5rem; display: flex; align-items: center; gap: 1rem;">
|
||
<button onclick="copyPromptToClipboard()" title="Copy full prompt to clipboard" style="background: #333; border: 1px solid #555; color: #aaa; cursor: pointer; padding: 0.25rem 0.6rem; border-radius: 4px; font-size: 0.8rem;">📋 Copy</button>
|
||
<label style="font-size: 0.82rem; color: #aaa; cursor: pointer; display: flex; align-items: center; gap: 0.3rem;">
|
||
<input type="checkbox" id="prompt-truncate-toggle" onchange="toggleMiddleTruncation()">
|
||
Truncate from middle
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Prompt display subsections -->
|
||
<div id="prompt-display" style="max-height: 60vh; overflow-y: auto; min-height: 3rem;"></div>
|
||
<!-- Hidden buffer for copy-to-clipboard raw text -->
|
||
<pre id="last-prompt" style="display: none;"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DM Management Tab Content -->
|
||
<div id="tab10" class="tab-content">
|
||
<div class="section">
|
||
<h3>📱 DM Management</h3>
|
||
<div style="margin-bottom: 1rem;">
|
||
<button onclick="loadDMUsers()">🔄 Refresh DM Users</button>
|
||
<button onclick="exportAllDMs()">📤 Export All DMs</button>
|
||
<button onclick="loadBlockedUsers()" style="background: #ff9800;">🚫 View Blocked Users</button>
|
||
</div>
|
||
<div style="margin-bottom: 1rem; padding: 1rem; background: #2a2a2a; border-radius: 4px;">
|
||
<h4 style="margin-top: 0;">📊 DM Interaction Analysis</h4>
|
||
<button onclick="runDailyAnalysis()" style="background: #9c27b0;">🔍 Run Daily Analysis Now</button>
|
||
<button onclick="viewAnalysisReports()" style="background: #673ab7;">📄 View All Reports</button>
|
||
<p style="font-size: 0.85rem; margin: 0.5rem 0 0 0; color: #aaa;">
|
||
Analysis runs automatically at 2 AM daily. Reports one user per day.
|
||
</p>
|
||
</div>
|
||
<div id="dm-users-list"></div>
|
||
|
||
<div class="section" id="blocked-users-section" style="display: none; margin-top: 2rem;">
|
||
<h4>🚫 Blocked Users</h4>
|
||
<div style="margin-bottom: 1rem;">
|
||
<button onclick="hideBlockedUsers()">← Back to DM Users</button>
|
||
</div>
|
||
<div id="blocked-users-list"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- LLM Settings Tab Content -->
|
||
<div id="tab4" class="tab-content">
|
||
<div class="section">
|
||
<h3>⚙️ Language Model Settings</h3>
|
||
<p>Configure language model behavior and language mode.</p>
|
||
|
||
<!-- Language Mode Section -->
|
||
<div style="margin-bottom: 1.5rem; padding: 1rem; background: #2a2a2a; border-radius: 4px; border: 2px solid #4a7bc9;">
|
||
<h4 style="margin-top: 0; color: #61dafb;">🌐 Language Mode</h4>
|
||
<p style="margin: 0.5rem 0; color: #aaa;">Switch Miku between English and Japanese responses.</p>
|
||
|
||
<div style="margin: 1rem 0;">
|
||
<div style="margin-bottom: 1rem;">
|
||
<strong>Current Language:</strong> <span id="current-language-display" style="color: #61dafb; font-weight: bold;">English</span>
|
||
</div>
|
||
<button onclick="toggleLanguageMode()" style="background: #4a7bc9; color: #fff; padding: 0.6rem 1.2rem; border: 2px solid #61dafb; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 1rem;">
|
||
🔄 Toggle Language (English ↔ Japanese)
|
||
</button>
|
||
</div>
|
||
|
||
<div style="margin-top: 1rem; padding: 1rem; background: #1a1a1a; border-radius: 4px; border-left: 3px solid #4a7bc9;">
|
||
<div style="font-size: 0.9rem;">
|
||
<div style="margin-bottom: 0.5rem;"><strong>English Mode:</strong></div>
|
||
<ul style="margin: 0 0 0.5rem 0; padding-left: 1.5rem; color: #aaa;">
|
||
<li>Uses standard Llama 3.1 model</li>
|
||
<li>Responds in English only</li>
|
||
</ul>
|
||
|
||
<div style="margin-bottom: 0.5rem;"><strong>Japanese Mode (日本語):</strong></div>
|
||
<ul style="margin: 0 0 0; padding-left: 1.5rem; color: #aaa;">
|
||
<li>Uses Llama 3.1 Swallow model (trained for Japanese)</li>
|
||
<li>Responds entirely in Japanese</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Language Mode Status Section -->
|
||
<div style="margin-bottom: 1.5rem; padding: 1rem; background: #2a2a2a; border-radius: 4px;">
|
||
<h4 style="margin-top: 0;">📊 Current Status</h4>
|
||
<div id="language-status-display" style="background: #1a1a1a; padding: 1rem; border-radius: 4px; font-family: monospace; font-size: 0.9rem;">
|
||
<p style="margin: 0.5rem 0;"><strong>Language Mode:</strong> <span id="status-language">English</span></p>
|
||
<p style="margin: 0.5rem 0;"><strong>Active Model:</strong> <span id="status-model">llama3.1</span></p>
|
||
<p style="margin: 0.5rem 0;"><strong>Available Languages:</strong> English, 日本語 (Japanese)</p>
|
||
</div>
|
||
<button onclick="refreshLanguageStatus()" style="margin-top: 1rem;">🔄 Refresh Status</button>
|
||
</div>
|
||
|
||
<!-- Information Section -->
|
||
<div style="padding: 1rem; background: #1a1a1a; border-radius: 4px; border-left: 3px solid #ff9800;">
|
||
<h4 style="margin-top: 0; color: #ff9800;">ℹ️ How Language Mode Works</h4>
|
||
<ul style="margin: 0.5rem 0; padding-left: 1.5rem; font-size: 0.9rem; color: #aaa;">
|
||
<li>English mode uses your default text model for English responses</li>
|
||
<li>Japanese mode switches to Swallow and responds only in 日本語</li>
|
||
<li>All personality traits, mood system, and features work in both modes</li>
|
||
<li>Language mode is global - affects all servers and DMs</li>
|
||
<li>Conversation history is preserved across language switches</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Image Generation Tab Content -->
|
||
<div id="tab5" class="tab-content">
|
||
<div class="section">
|
||
<h3>🎨 Image Generation System</h3>
|
||
<p>Natural language image generation powered by ComfyUI. Users can ask Miku to create images naturally without commands!</p>
|
||
|
||
<!-- Status Section -->
|
||
<div style="margin-bottom: 1.5rem;">
|
||
<h4>System Status</h4>
|
||
<div id="image-system-status" style="margin-bottom: 1rem;">
|
||
<button onclick="checkImageSystemStatus()">🔄 Check Status</button>
|
||
</div>
|
||
<div id="image-status-display" style="background: #2a2a2a; padding: 1rem; border-radius: 4px; font-family: monospace; font-size: 0.9rem;"></div>
|
||
</div>
|
||
|
||
<!-- Detection Testing -->
|
||
<div style="margin-bottom: 1.5rem;">
|
||
<h4>Test Natural Language Detection</h4>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="detection-test-message">Test Message:</label>
|
||
<textarea id="detection-test-message" placeholder="e.g. Hey Miku, I'd like to see you swimming in a pool" rows="2" style="width: 100%; margin-top: 0.5rem;"></textarea>
|
||
</div>
|
||
<button onclick="testImageDetection()" style="margin-right: 0.5rem;">🔍 Test Detection</button>
|
||
<div id="detection-test-results" style="margin-top: 0.5rem; font-size: 0.9rem;"></div>
|
||
</div>
|
||
|
||
<!-- Manual Image Generation -->
|
||
<div style="margin-bottom: 1.5rem;">
|
||
<h4>Manual Image Generation</h4>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="manual-image-prompt">Image Prompt:</label>
|
||
<textarea id="manual-image-prompt" placeholder="Describe the image you want to generate..." rows="3" style="width: 100%; margin-top: 0.5rem;"></textarea>
|
||
</div>
|
||
<button onclick="generateManualImage()" style="margin-right: 0.5rem;">🎨 Generate Image</button>
|
||
<div id="manual-image-status" style="margin-top: 0.5rem; font-size: 0.9rem;"></div>
|
||
<div id="manual-image-preview" style="margin-top: 1rem; text-align: center;"></div>
|
||
</div>
|
||
|
||
<!-- System Information -->
|
||
<div style="margin-bottom: 1.5rem;">
|
||
<h4>Image Generation Settings</h4>
|
||
<div style="background: #2a2a2a; padding: 1rem; border-radius: 4px;">
|
||
<div style="margin-bottom: 0.5rem;"><strong>ComfyUI Configuration:</strong></div>
|
||
<ul style="margin: 0; padding-left: 1.5rem;">
|
||
<li>URL: Auto-detected (tries multiple Docker networking options)</li>
|
||
<li>Workflow Template: <code>Miku_BasicWorkflow.json</code></li>
|
||
<li>Host Output Directory: <code>/home/koko210Serve/ComfyUI/output/</code></li>
|
||
<li>Container Mount Point: <code>/app/ComfyUI/output/</code></li>
|
||
<li>Generation Timeout: 300 seconds</li>
|
||
</ul>
|
||
<div style="margin-top: 1rem; font-size: 0.9rem; color: #aaa;">
|
||
<strong>Note:</strong> Make sure ComfyUI is running and the workflow template exists in the bot directory.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Autonomous Stats Tab Content -->
|
||
<div id="tab6" class="tab-content">
|
||
<div class="section">
|
||
<h3>📊 Autonomous V2 Decision Engine Stats</h3>
|
||
<p>Real-time monitoring of Miku's autonomous decision-making context and mood-based personality stats.</p>
|
||
|
||
<div style="margin-bottom: 1.5rem;">
|
||
<label for="autonomous-server-select">Select Server:</label>
|
||
<select id="autonomous-server-select" onchange="loadAutonomousStats()">
|
||
<option value="">-- Select a server --</option>
|
||
</select>
|
||
<button onclick="loadAutonomousStats()" style="margin-left: 0.5rem;">🔄 Refresh</button>
|
||
</div>
|
||
|
||
<div id="autonomous-stats-display"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Chat with LLM Tab Content -->
|
||
<div id="tab7" class="tab-content">
|
||
<div class="section">
|
||
<h3>💬 Chat with LLM</h3>
|
||
<p>Direct chat interface with the language models. Test responses, experiment with prompts, or just chat with Miku!</p>
|
||
|
||
<!-- Configuration Options -->
|
||
<div style="background: #2a2a2a; padding: 1.5rem; border-radius: 8px; margin-bottom: 1.5rem;">
|
||
<h4 style="margin-top: 0; color: #61dafb;">⚙️ Chat Configuration</h4>
|
||
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1.5rem; margin-bottom: 1rem;">
|
||
<!-- Model Selection -->
|
||
<div>
|
||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">🤖 Model Type:</label>
|
||
<div style="display: flex; gap: 1rem;">
|
||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||
<input type="radio" name="chat-model-type" value="text" checked onchange="toggleChatImageUpload()">
|
||
<span style="margin-left: 0.5rem;">💬 Text Model (Fast)</span>
|
||
</label>
|
||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||
<input type="radio" name="chat-model-type" value="vision" onchange="toggleChatImageUpload()">
|
||
<span style="margin-left: 0.5rem;">👁️ Vision Model (Images)</span>
|
||
</label>
|
||
</div>
|
||
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
|
||
Text model for conversations, Vision model for image analysis
|
||
</div>
|
||
</div>
|
||
|
||
<!-- System Prompt Toggle -->
|
||
<div>
|
||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">🎭 System Prompt:</label>
|
||
<div style="display: flex; gap: 1rem;">
|
||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||
<input type="radio" name="chat-system-prompt" value="true" checked>
|
||
<span style="margin-left: 0.5rem;">✅ Use Miku Personality</span>
|
||
</label>
|
||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||
<input type="radio" name="chat-system-prompt" value="false">
|
||
<span style="margin-left: 0.5rem;">❌ Raw LLM (No Prompt)</span>
|
||
</label>
|
||
</div>
|
||
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
|
||
With prompt: Chat as Miku. Without: Direct LLM responses
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Mood Selection -->
|
||
<div>
|
||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">😊 Miku's Mood:</label>
|
||
<select id="chat-mood-select" style="width: 100%; padding: 0.5rem; background: #333; color: #fff; border: 1px solid #555; border-radius: 4px;">
|
||
<option value="neutral" selected>neutral</option>
|
||
</select>
|
||
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
|
||
Choose Miku's emotional state for this conversation
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Image Upload for Vision Model -->
|
||
<div id="chat-image-upload-section" style="display: none; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #444;">
|
||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">🖼️ Upload Image:</label>
|
||
<input type="file" id="chat-image-file" accept="image/*" style="margin-bottom: 0.5rem;">
|
||
<div style="font-size: 0.85rem; color: #aaa;">
|
||
Upload an image for the vision model to analyze
|
||
</div>
|
||
<div id="chat-image-preview" style="margin-top: 0.5rem; display: none;">
|
||
<img id="chat-image-preview-img" style="max-width: 200px; max-height: 200px; border: 1px solid #555; border-radius: 4px;">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Clear Chat Button -->
|
||
<div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #444;">
|
||
<button onclick="clearChatHistory()" style="background: #ff9800;">🗑️ Clear Chat History</button>
|
||
<span style="margin-left: 1rem; font-size: 0.85rem; color: #aaa;">Remove all messages from this session</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Chat Messages Container -->
|
||
<div id="chat-messages" style="background: #1e1e1e; border: 1px solid #444; border-radius: 8px; padding: 1rem; min-height: 400px; max-height: 500px; overflow-y: auto; margin-bottom: 1rem;">
|
||
<div style="text-align: center; color: #888; padding: 2rem;">
|
||
💬 Start chatting with the LLM! Your conversation will appear here.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Chat Input Area -->
|
||
<div style="display: flex; gap: 0.5rem; align-items: flex-end;">
|
||
<div style="flex: 1;">
|
||
<textarea
|
||
id="chat-input"
|
||
placeholder="Type your message here..."
|
||
rows="3"
|
||
style="width: 100%; padding: 0.75rem; background: #2a2a2a; color: #fff; border: 1px solid #555; border-radius: 4px; font-family: inherit; resize: vertical;"
|
||
onkeydown="handleChatKeyPress(event)"
|
||
></textarea>
|
||
</div>
|
||
<div>
|
||
<button
|
||
id="chat-send-btn"
|
||
onclick="sendChatMessage()"
|
||
style="padding: 1rem 1.5rem; height: 100%; background: #4CAF50; font-size: 1rem; font-weight: bold;"
|
||
>
|
||
📤 Send
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="margin-top: 0.5rem; font-size: 0.85rem; color: #aaa;">
|
||
💡 Tip: Press Ctrl+Enter to send your message quickly
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab 8: Voice Call Management -->
|
||
<div id="tab8" class="tab-content">
|
||
<div class="section">
|
||
<h3>📞 Initiate Voice Call</h3>
|
||
<p>Start an automated voice chat session with a user. Miku will automatically manage containers, join voice chat, and send an invitation DM.</p>
|
||
|
||
<div style="background: #2a2a2a; padding: 1.5rem; border-radius: 8px; margin-bottom: 1.5rem;">
|
||
<h4 style="margin-top: 0; color: #61dafb;">⚙️ Voice Call Configuration</h4>
|
||
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 1.5rem;">
|
||
<!-- User ID Input -->
|
||
<div>
|
||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">👤 Target User ID:</label>
|
||
<input
|
||
type="text"
|
||
id="voice-user-id"
|
||
placeholder="Discord user ID (e.g., 123456789)"
|
||
style="width: 100%; padding: 0.5rem; background: #333; color: #fff; border: 1px solid #555; border-radius: 4px; box-sizing: border-box;"
|
||
>
|
||
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
|
||
Discord ID of the user to call
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Voice Channel ID Input -->
|
||
<div>
|
||
<label style="display: block; margin-bottom: 0.5rem; font-weight: bold;">🎤 Voice Channel ID:</label>
|
||
<input
|
||
type="text"
|
||
id="voice-channel-id"
|
||
placeholder="Discord channel ID (e.g., 987654321)"
|
||
style="width: 100%; padding: 0.5rem; background: #333; color: #fff; border: 1px solid #555; border-radius: 4px; box-sizing: border-box;"
|
||
>
|
||
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.3rem;">
|
||
Discord ID of the voice channel to join
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Debug Mode Toggle -->
|
||
<div style="margin-bottom: 1.5rem; padding: 1rem; background: #1e1e1e; border-radius: 4px;">
|
||
<label style="display: flex; align-items: center; cursor: pointer;">
|
||
<input
|
||
type="checkbox"
|
||
id="voice-debug-mode"
|
||
style="margin-right: 0.7rem; width: 18px; height: 18px; cursor: pointer;"
|
||
>
|
||
<span style="font-weight: bold;">🐛 Debug Mode</span>
|
||
</label>
|
||
<div style="font-size: 0.85rem; color: #aaa; margin-top: 0.5rem; margin-left: 1.7rem;">
|
||
When enabled, shows voice transcriptions and responses in text channel. When disabled, voice chat is private.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Call Status Display -->
|
||
<div id="voice-call-status" style="background: #1e1e1e; padding: 1rem; border-radius: 4px; margin-bottom: 1.5rem; display: none;">
|
||
<div style="color: #61dafb; font-weight: bold; margin-bottom: 0.5rem;">📊 Call Status:</div>
|
||
<div id="voice-call-status-text" style="color: #aaa; font-size: 0.9rem;"></div>
|
||
<div id="voice-call-invite-link" style="margin-top: 0.5rem; display: none;">
|
||
<strong>Invite Link:</strong> <a id="voice-call-invite-url" href="" target="_blank" style="color: #61dafb;">View Invite</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Call Buttons -->
|
||
<div style="display: flex; gap: 1rem;">
|
||
<button
|
||
id="voice-call-btn"
|
||
onclick="initiateVoiceCall()"
|
||
style="background: #2ecc71; color: #000; padding: 0.7rem 1.5rem; border: 1px solid #27ae60; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 1rem;"
|
||
>
|
||
📞 Initiate Call
|
||
</button>
|
||
<button
|
||
id="voice-call-cancel-btn"
|
||
onclick="cancelVoiceCall()"
|
||
style="background: #e74c3c; color: #fff; padding: 0.7rem 1.5rem; border: 1px solid #c0392b; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 1rem; display: none;"
|
||
>
|
||
🛑 Cancel Call
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Call Information -->
|
||
<div style="background: #1a1a2e; padding: 1.5rem; border-radius: 8px; border-left: 3px solid #61dafb;">
|
||
<h4 style="margin-top: 0; color: #61dafb;">ℹ️ How Voice Calls Work</h4>
|
||
<ul style="color: #ddd; line-height: 1.8;">
|
||
<li><strong>Automatic Setup:</strong> STT and TTS containers start automatically</li>
|
||
<li><strong>Warmup Wait:</strong> System waits for both containers to be ready (~30-75 seconds)</li>
|
||
<li><strong>VC Join:</strong> Miku joins the specified voice channel</li>
|
||
<li><strong>DM Invitation:</strong> User receives a personalized invite DM with a voice channel link</li>
|
||
<li><strong>Auto-Listen:</strong> STT automatically starts when user joins</li>
|
||
<li><strong>Auto-Leave:</strong> Miku leaves 45 seconds after user disconnects</li>
|
||
<li><strong>Timeout:</strong> If user doesn't join within 30 minutes, call is cancelled</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Call History -->
|
||
<div style="margin-top: 2rem;">
|
||
<h4 style="color: #61dafb; margin-bottom: 1rem;">📋 Recent Calls</h4>
|
||
<div id="voice-call-history" style="background: #1e1e1e; border: 1px solid #444; border-radius: 4px; padding: 1rem;">
|
||
<div style="text-align: center; color: #888;">No calls yet. Start one above!</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab 9: Memory Management -->
|
||
<div id="tab9" class="tab-content">
|
||
<div class="section">
|
||
<h3>🧠 Cheshire Cat Memory Management</h3>
|
||
<p style="color: #aaa; margin-bottom: 1rem;">
|
||
Manage Miku's long-term memories powered by the Cheshire Cat AI pipeline.
|
||
Memories are stored in Qdrant vector database and used to give Miku persistent knowledge about users.
|
||
</p>
|
||
|
||
<!-- Cat Integration Status -->
|
||
<div id="cat-status-section" style="background: #1a1a2e; border: 1px solid #444; border-radius: 8px; padding: 1rem; margin-bottom: 1.5rem;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||
<div>
|
||
<h4 style="margin: 0 0 0.3rem 0;">🐱 Cheshire Cat Status</h4>
|
||
<span id="cat-status-indicator" style="color: #888;">Checking...</span>
|
||
</div>
|
||
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||
<button id="cat-toggle-btn" onclick="toggleCatIntegration()" style="background: #333; color: #fff; padding: 0.4rem 0.8rem; border: 2px solid #666; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.85rem;">
|
||
Loading...
|
||
</button>
|
||
<button onclick="refreshMemoryStats()" style="background: #2a5599; color: #fff; padding: 0.4rem 0.8rem; border: none; border-radius: 4px; cursor: pointer;">
|
||
🔄 Refresh
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Memory Statistics -->
|
||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem;">
|
||
<div id="stat-episodic" style="background: #1a2332; border: 1px solid #2a5599; border-radius: 8px; padding: 1rem; text-align: center;">
|
||
<div style="font-size: 2rem; font-weight: bold; color: #61dafb;" id="stat-episodic-count">—</div>
|
||
<div style="color: #aaa; font-size: 0.85rem;">📝 Episodic Memories</div>
|
||
<div style="color: #666; font-size: 0.75rem; margin-top: 0.3rem;">Conversation snippets</div>
|
||
</div>
|
||
<div id="stat-declarative" style="background: #1a3322; border: 1px solid #2a9955; border-radius: 8px; padding: 1rem; text-align: center;">
|
||
<div style="font-size: 2rem; font-weight: bold; color: #6fdc6f;" id="stat-declarative-count">—</div>
|
||
<div style="color: #aaa; font-size: 0.85rem;">📚 Declarative Facts</div>
|
||
<div style="color: #666; font-size: 0.75rem; margin-top: 0.3rem;">Learned knowledge</div>
|
||
</div>
|
||
<div id="stat-procedural" style="background: #332a1a; border: 1px solid #995e2a; border-radius: 8px; padding: 1rem; text-align: center;">
|
||
<div style="font-size: 2rem; font-weight: bold; color: #dcb06f;" id="stat-procedural-count">—</div>
|
||
<div style="color: #aaa; font-size: 0.85rem;">⚙️ Procedural</div>
|
||
<div style="color: #666; font-size: 0.75rem; margin-top: 0.3rem;">Tools & procedures</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Consolidation -->
|
||
<div style="background: #1a1a2e; border: 1px solid #444; border-radius: 8px; padding: 1rem; margin-bottom: 1.5rem;">
|
||
<h4 style="margin: 0 0 0.5rem 0;">🌙 Memory Consolidation</h4>
|
||
<p style="color: #aaa; font-size: 0.85rem; margin-bottom: 0.75rem;">
|
||
Trigger the sleep consolidation process: analyzes episodic memories, extracts important facts, and removes trivial entries.
|
||
</p>
|
||
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||
<button id="consolidate-btn" onclick="triggerConsolidation()" style="background: #5b3a8c; color: #fff; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||
🌙 Run Consolidation
|
||
</button>
|
||
<span id="consolidation-status" style="color: #888; font-size: 0.85rem;"></span>
|
||
</div>
|
||
<div id="consolidation-result" style="display: none; margin-top: 0.75rem; background: #111; border: 1px solid #333; border-radius: 4px; padding: 0.75rem; font-size: 0.85rem; color: #ccc; white-space: pre-wrap; max-height: 200px; overflow-y: auto;"></div>
|
||
</div>
|
||
|
||
<!-- Declarative Facts Browser -->
|
||
<div style="background: #1a1a2e; border: 1px solid #444; border-radius: 8px; padding: 1rem; margin-bottom: 1.5rem;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem;">
|
||
<h4 style="margin: 0;">📚 Declarative Facts</h4>
|
||
<div style="display: flex; gap: 0.5rem;">
|
||
<button onclick="showCreateMemoryModal('declarative')" style="background: #2a9955; color: #fff; padding: 0.3rem 0.7rem; border: none; border-radius: 4px; cursor: pointer; font-size: 0.85rem;">
|
||
➕ Add Fact
|
||
</button>
|
||
<button onclick="loadFacts()" style="background: #2a5599; color: #fff; padding: 0.3rem 0.7rem; border: none; border-radius: 4px; cursor: pointer; font-size: 0.85rem;">
|
||
🔄 Load Facts
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div style="margin-bottom: 0.5rem;">
|
||
<input type="text" id="facts-search" placeholder="🔍 Search facts..."
|
||
oninput="filterMemories('facts-list', this.value)"
|
||
style="width: 100%; padding: 0.4rem; background: #242424; color: #fff; border: 1px solid #444; border-radius: 4px; box-sizing: border-box;">
|
||
</div>
|
||
<div id="facts-list" style="max-height: 400px; overflow-y: auto;">
|
||
<div style="text-align: center; color: #666; padding: 2rem;">Click "Load Facts" to view stored knowledge</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Episodic Memories Browser -->
|
||
<div style="background: #1a1a2e; border: 1px solid #444; border-radius: 8px; padding: 1rem; margin-bottom: 1.5rem;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem;">
|
||
<h4 style="margin: 0;">📝 Episodic Memories</h4>
|
||
<div style="display: flex; gap: 0.5rem;">
|
||
<button onclick="showCreateMemoryModal('episodic')" style="background: #2a9955; color: #fff; padding: 0.3rem 0.7rem; border: none; border-radius: 4px; cursor: pointer; font-size: 0.85rem;">
|
||
➕ Add Memory
|
||
</button>
|
||
<button onclick="loadEpisodicMemories()" style="background: #2a5599; color: #fff; padding: 0.3rem 0.7rem; border: none; border-radius: 4px; cursor: pointer; font-size: 0.85rem;">
|
||
🔄 Load Memories
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div style="margin-bottom: 0.5rem;">
|
||
<input type="text" id="episodic-search" placeholder="🔍 Search memories..."
|
||
oninput="filterMemories('episodic-list', this.value)"
|
||
style="width: 100%; padding: 0.4rem; background: #242424; color: #fff; border: 1px solid #444; border-radius: 4px; box-sizing: border-box;">
|
||
</div>
|
||
<div id="episodic-list" style="max-height: 400px; overflow-y: auto;">
|
||
<div style="text-align: center; color: #666; padding: 2rem;">Click "Load Memories" to view conversation snippets</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- DANGER ZONE: Delete All Memories -->
|
||
<div style="background: #2e1a1a; border: 2px solid #993333; border-radius: 8px; padding: 1rem;">
|
||
<h4 style="margin: 0 0 0.5rem 0; color: #ff6b6b;">⚠️ Danger Zone — Delete All Memories</h4>
|
||
<p style="color: #cc9999; font-size: 0.85rem; margin-bottom: 1rem;">
|
||
This will permanently erase ALL of Miku's memories — episodic conversations, learned facts, everything.
|
||
This action is <strong>irreversible</strong>. Miku will forget everything she has ever learned.
|
||
</p>
|
||
|
||
<!-- Step 1: Initial checkbox -->
|
||
<div id="delete-step-1" style="margin-bottom: 0.75rem;">
|
||
<label style="cursor: pointer; color: #ff9999;">
|
||
<input type="checkbox" id="delete-checkbox-1" onchange="onDeleteStep1Change()">
|
||
I understand this will permanently delete all of Miku's memories
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Step 2: Second confirmation (hidden initially) -->
|
||
<div id="delete-step-2" style="display: none; margin-bottom: 0.75rem;">
|
||
<label style="cursor: pointer; color: #ff9999;">
|
||
<input type="checkbox" id="delete-checkbox-2" onchange="onDeleteStep2Change()">
|
||
I confirm this is irreversible and I want to proceed
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Step 3: Type confirmation string (hidden initially) -->
|
||
<div id="delete-step-3" style="display: none; margin-bottom: 0.75rem;">
|
||
<p style="color: #ff6b6b; font-size: 0.85rem; margin-bottom: 0.5rem;">
|
||
Type exactly: <code style="background: #333; padding: 0.2rem 0.4rem; border-radius: 3px; color: #ff9999;">Yes, I am deleting Miku's memories fully.</code>
|
||
</p>
|
||
<input type="text" id="delete-confirmation-input" placeholder="Type the confirmation string..."
|
||
style="width: 100%; padding: 0.5rem; background: #1a1a1a; color: #ff9999; border: 1px solid #993333; border-radius: 4px; font-family: monospace; box-sizing: border-box;"
|
||
oninput="onDeleteInputChange()">
|
||
</div>
|
||
|
||
<!-- Final delete button (hidden initially) -->
|
||
<div id="delete-step-final" style="display: none;">
|
||
<button id="delete-all-btn" onclick="executeDeleteAllMemories()" disabled
|
||
style="background: #cc3333; color: #fff; padding: 0.5rem 1.5rem; border: none; border-radius: 4px; cursor: not-allowed; font-weight: bold; opacity: 0.5;">
|
||
🗑️ Permanently Delete All Memories
|
||
</button>
|
||
<button onclick="resetDeleteFlow()" style="background: #444; color: #ccc; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; margin-left: 0.5rem;">
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab 11: Profile Picture Management -->
|
||
<div id="tab11" class="tab-content">
|
||
<div class="section">
|
||
<h3>🖼️ Profile Picture Management</h3>
|
||
<p style="font-size: 0.9rem; color: #aaa;">Change, crop, and manage Miku's profile picture. Edit descriptions and role colors.</p>
|
||
|
||
<!-- Action Buttons -->
|
||
<div style="margin-bottom: 1rem; display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;">
|
||
<button onclick="pfpChangeDanbooru()">🎨 Change (Danbooru)</button>
|
||
<button onclick="pfpRestoreFallback()">🔄 Restore Original Avatar</button>
|
||
</div>
|
||
|
||
<!-- Upload Section -->
|
||
<div style="margin-bottom: 1rem;">
|
||
<label for="pfp-tab-upload">Upload Custom Image:</label>
|
||
<input type="file" id="pfp-tab-upload" accept="image/*" style="margin-left: 0.5rem;">
|
||
<button onclick="pfpUploadCustom()">📤 Upload & Apply</button>
|
||
<div style="font-size: 0.8rem; color: #888; margin-top: 0.3rem; margin-left: 0.5rem;">
|
||
💡 Supports static images (PNG, JPG) and animated GIFs |
|
||
⚠️ Animated GIFs require Discord Nitro on the bot account
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Album / Gallery -->
|
||
<div class="album-section">
|
||
<div class="album-header" onclick="albumToggle()">
|
||
<h4><span id="album-toggle-icon">▶</span> 📚 Profile Picture Album <span id="album-count" style="color: #888; font-weight: normal;"></span></h4>
|
||
<span class="album-disk-usage" id="album-disk-usage"></span>
|
||
</div>
|
||
<div id="album-body" style="display: none;">
|
||
<!-- Toolbar -->
|
||
<div class="album-toolbar">
|
||
<input type="file" id="album-upload" accept="image/*" multiple style="max-width: 220px;">
|
||
<button onclick="albumUpload()">📤 Add to Album</button>
|
||
<button onclick="albumAddCurrent()">📌 Archive Current PFP</button>
|
||
<button onclick="albumBulkDelete()" style="background: #c0392b;" id="album-bulk-delete-btn" disabled>🗑️ Delete Selected (<span id="album-selected-count">0</span>)</button>
|
||
<div id="album-status" style="font-size: 0.85rem; color: #61dafb; margin-left: 0.5rem;"></div>
|
||
</div>
|
||
<!-- Grid -->
|
||
<div class="album-grid" id="album-grid"></div>
|
||
<!-- Detail panel (shown when an entry is clicked) -->
|
||
<div class="album-detail" id="album-detail" style="display: none;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||
<h4 style="margin: 0;">Selected Entry</h4>
|
||
<button onclick="albumCloseDetail()" style="background: #555; padding: 0.3rem 0.6rem;">✖ Close</button>
|
||
</div>
|
||
<div class="album-detail-previews">
|
||
<div class="pfp-preview-box">
|
||
<span class="label">📷 Original</span>
|
||
<img id="album-detail-original" src="" alt="Original" style="cursor: pointer;" onclick="albumShowCropInterface()" title="Click to crop">
|
||
<div id="album-detail-dims" style="font-size: 0.8rem; color: #666; margin-top: 0.3rem;"></div>
|
||
</div>
|
||
<div class="pfp-preview-box">
|
||
<span class="label">🎯 Cropped</span>
|
||
<img id="album-detail-cropped" src="" alt="Cropped" style="border-radius: 50%; max-width: 256px; max-height: 256px;">
|
||
</div>
|
||
</div>
|
||
<!-- Album entry crop interface -->
|
||
<div id="album-crop-section" style="display: none; margin: 1rem 0; padding: 1rem; background: #1a1a2e; border: 1px solid #444; border-radius: 8px;">
|
||
<h4 style="margin-top: 0;">✂️ Crop Album Entry</h4>
|
||
<div class="pfp-crop-container">
|
||
<img id="album-crop-image" src="" alt="Crop source">
|
||
</div>
|
||
<div style="margin-top: 0.75rem; display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
||
<button onclick="albumApplyManualCrop()" style="background: #4CAF50; color: #fff; font-weight: bold;">✂️ Apply Crop</button>
|
||
<button onclick="albumApplyAutoCrop()">🤖 Auto Crop</button>
|
||
<button onclick="albumHideCropInterface()" style="background: #666;">✖ Cancel</button>
|
||
</div>
|
||
</div>
|
||
<!-- Description -->
|
||
<div style="margin-top: 1rem;">
|
||
<label style="color: #aaa;">📝 Description:</label>
|
||
<textarea id="album-detail-description" class="pfp-description-editor" style="min-height: 80px;"></textarea>
|
||
<div style="margin-top: 0.5rem; display: flex; gap: 0.5rem;">
|
||
<button onclick="albumSaveDescription()" style="background: #4CAF50; color: #fff;">💾 Save</button>
|
||
</div>
|
||
</div>
|
||
<!-- Actions -->
|
||
<div style="margin-top: 1rem; display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
||
<button onclick="albumSetAsCurrent()" style="background: #2196F3; color: #fff; font-weight: bold;">🖼️ Set as Current PFP</button>
|
||
<button onclick="albumDeleteSelected()" style="background: #c0392b; color: #fff;">🗑️ Delete Entry</button>
|
||
</div>
|
||
<div id="album-detail-meta" style="margin-top: 0.75rem; font-size: 0.8rem; color: #888;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Crop Mode Toggle -->
|
||
<div class="crop-mode-toggle">
|
||
<span style="color: #61dafb; font-weight: bold;">Crop Mode:</span>
|
||
<label>
|
||
<input type="radio" name="pfp-crop-mode" value="auto" checked>
|
||
🤖 Auto (face detection)
|
||
</label>
|
||
<label>
|
||
<input type="radio" name="pfp-crop-mode" value="manual">
|
||
✂️ Manual
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Status -->
|
||
<div id="pfp-tab-status" style="margin-top: 0.5rem; font-size: 0.9rem; color: #61dafb;"></div>
|
||
|
||
<!-- Manual Crop Interface (hidden by default) -->
|
||
<div id="pfp-crop-section" style="display: none; margin: 1rem 0; padding: 1rem; background: #1a1a2e; border: 1px solid #444; border-radius: 8px;">
|
||
<h4 style="margin-top: 0;">✂️ Manual Crop</h4>
|
||
<p style="font-size: 0.85rem; color: #aaa; margin-bottom: 0.5rem;">
|
||
Drag to select a square crop region. Discord avatars are displayed as circles, so keep the subject centered.
|
||
</p>
|
||
<div class="pfp-crop-container">
|
||
<img id="pfp-crop-image" src="" alt="Crop source">
|
||
</div>
|
||
<div style="margin-top: 0.75rem; display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
||
<button onclick="pfpApplyManualCrop()" style="background: #4CAF50; color: #fff; font-weight: bold;">✂️ Apply Crop</button>
|
||
<button onclick="pfpApplyAutoCrop()">🤖 Use Auto Crop Instead</button>
|
||
<button onclick="pfpHideCropInterface()" style="background: #666;">✖ Cancel</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Image Previews -->
|
||
<div class="pfp-preview-container">
|
||
<div class="pfp-preview-box">
|
||
<span class="label">📷 Original (full resolution)</span>
|
||
<img id="pfp-preview-original" src="" alt="Original" style="cursor: pointer;" onclick="pfpRecrop()" title="Click to re-crop">
|
||
<div id="pfp-original-dims" style="font-size: 0.8rem; color: #666; margin-top: 0.3rem;"></div>
|
||
</div>
|
||
<div class="pfp-preview-box">
|
||
<span class="label">🎯 Current Avatar (cropped)</span>
|
||
<img id="pfp-preview-current" src="" alt="Current avatar" style="border-radius: 50%; max-width: 256px; max-height: 256px;">
|
||
<div style="font-size: 0.8rem; color: #666; margin-top: 0.3rem;">512×512 · displayed as circle</div>
|
||
<button onclick="pfpRecrop()" style="margin-top: 0.5rem;">✂️ Re-crop Current</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Role Color Management -->
|
||
<div style="margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #333;">
|
||
<h4>🎨 Role Color Management</h4>
|
||
<p style="font-size: 0.9rem; color: #aaa;">Manually set Miku's role color or reset to fallback (#86cecb)</p>
|
||
|
||
<div style="margin-bottom: 1rem; display: flex; gap: 10px; align-items: end;">
|
||
<div>
|
||
<label for="pfp-tab-role-color-hex">Hex Color:</label>
|
||
<input type="text" id="pfp-tab-role-color-hex" placeholder="#86cecb" maxlength="7" style="width: 100px; font-family: monospace;">
|
||
</div>
|
||
<button onclick="setCustomRoleColor()">🎨 Apply Color</button>
|
||
<button onclick="resetRoleColor()">🔄 Reset to Fallback</button>
|
||
</div>
|
||
|
||
<div id="pfp-tab-role-color-status" style="margin-top: 0.5rem; font-size: 0.9rem; color: #61dafb;"></div>
|
||
</div>
|
||
|
||
<!-- Description Editor -->
|
||
<div style="margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #333;">
|
||
<h4>📝 Profile Picture Description</h4>
|
||
<p style="font-size: 0.9rem; color: #aaa;">Edit the description used for context when users ask about Miku's avatar. Saved to Cheshire Cat memory.</p>
|
||
<textarea id="pfp-description-editor" class="pfp-description-editor" placeholder="Loading description..."></textarea>
|
||
<div style="margin-top: 0.5rem; display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
||
<button onclick="pfpSaveDescription()" style="background: #4CAF50; color: #fff;">💾 Save Description</button>
|
||
<button onclick="pfpRegenerateDescription()">🔄 Re-generate (Vision Model)</button>
|
||
</div>
|
||
<div id="pfp-desc-status" style="margin-top: 0.5rem; font-size: 0.9rem; color: #61dafb;"></div>
|
||
</div>
|
||
|
||
<!-- Metadata (bottom) -->
|
||
<div id="pfp-tab-metadata" style="margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #333; background: #1e1e1e; padding: 0.5rem; border: 1px solid #333; display: none;">
|
||
<h4 style="margin-top: 0;">Current Profile Picture Info:</h4>
|
||
<pre id="pfp-tab-metadata-content" style="margin: 0;"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div class="logs" id="logs-panel">
|
||
<h3>Logs</h3>
|
||
<div id="logs-paused-banner" class="logs-paused-indicator" onclick="scrollLogsToBottom()">⏸ Auto-scroll paused — click to resume</div>
|
||
<div id="logs-content"></div>
|
||
</div>
|
||
|
||
<div id="notification"></div>
|
||
|
||
<!-- Edit Memory Modal -->
|
||
<div id="edit-memory-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 2000; align-items: center; justify-content: center;">
|
||
<div style="background: #1e1e1e; border: 2px solid #555; border-radius: 8px; padding: 2rem; max-width: 600px; width: 90%;">
|
||
<h3 style="margin: 0 0 1rem 0; color: #61dafb;">✏️ Edit Memory</h3>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label style="display: block; color: #ccc; margin-bottom: 0.5rem;">Content:</label>
|
||
<textarea id="edit-memory-content" rows="5" style="width: 100%; padding: 0.5rem; background: #2a2a2a; color: #fff; border: 1px solid #555; border-radius: 4px; font-family: monospace; box-sizing: border-box; resize: vertical;"></textarea>
|
||
</div>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label style="display: block; color: #ccc; margin-bottom: 0.5rem;">Source:</label>
|
||
<input type="text" id="edit-memory-source" style="width: 100%; padding: 0.5rem; background: #2a2a2a; color: #fff; border: 1px solid #555; border-radius: 4px; box-sizing: border-box;">
|
||
</div>
|
||
<div style="display: flex; gap: 0.5rem; justify-content: flex-end;">
|
||
<button onclick="closeEditMemoryModal()" style="background: #444; color: #ccc; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer;">
|
||
Cancel
|
||
</button>
|
||
<button onclick="saveMemoryEdit()" style="background: #2a5599; color: #fff; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||
💾 Save Changes
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Create Memory Modal -->
|
||
<div id="create-memory-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 2000; align-items: center; justify-content: center;">
|
||
<div style="background: #1e1e1e; border: 2px solid #555; border-radius: 8px; padding: 2rem; max-width: 600px; width: 90%;">
|
||
<input type="hidden" id="create-memory-collection" value="">
|
||
<h3 style="margin: 0 0 1rem 0; color: #6fdc6f;" id="create-modal-title">➕ Create New Memory</h3>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label style="display: block; color: #ccc; margin-bottom: 0.5rem;">Content:</label>
|
||
<textarea id="create-memory-content" rows="5" placeholder="Enter the fact or memory content..." style="width: 100%; padding: 0.5rem; background: #2a2a2a; color: #fff; border: 1px solid #555; border-radius: 4px; font-family: monospace; box-sizing: border-box; resize: vertical;"></textarea>
|
||
</div>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label style="display: block; color: #ccc; margin-bottom: 0.5rem;">User ID (optional):</label>
|
||
<input type="text" id="create-memory-user-id" placeholder="e.g., discord_123456789" style="width: 100%; padding: 0.5rem; background: #2a2a2a; color: #fff; border: 1px solid #555; border-radius: 4px; box-sizing: border-box;">
|
||
<small style="color: #888;">Leave empty for general facts, or specify a Discord user ID for user-specific facts</small>
|
||
</div>
|
||
<div style="margin-bottom: 1rem;">
|
||
<label style="display: block; color: #ccc; margin-bottom: 0.5rem;">Source (optional):</label>
|
||
<input type="text" id="create-memory-source" placeholder="e.g., manual_admin, wiki, etc." style="width: 100%; padding: 0.5rem; background: #2a2a2a; color: #fff; border: 1px solid #555; border-radius: 4px; box-sizing: border-box;">
|
||
</div>
|
||
<div style="display: flex; gap: 0.5rem; justify-content: flex-end;">
|
||
<button onclick="closeCreateMemoryModal()" style="background: #444; color: #ccc; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer;">
|
||
Cancel
|
||
</button>
|
||
<button onclick="saveNewMemory()" style="background: #2a9955; color: #fff; padding: 0.5rem 1rem; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
|
||
✨ Create Memory
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/static/js/core.js?v=20260502"></script>
|
||
<script src="/static/js/servers.js?v=20260502"></script>
|
||
<script src="/static/js/modes.js?v=20260502"></script>
|
||
<script src="/static/js/actions.js?v=20260502"></script>
|
||
<script src="/static/js/image-gen.js?v=20260502"></script>
|
||
<script src="/static/js/status.js?v=20260502"></script>
|
||
<script src="/static/js/dm.js?v=20260502"></script>
|
||
<script src="/static/js/chat.js?v=20260502"></script>
|
||
<script src="/static/js/memories.js?v=20260502"></script>
|
||
<script src="/static/js/profile.js?v=20260502"></script>
|
||
</body>
|
||
</html>
|