feat: add active features indicator badges in header

Add visual badges in header showing which roleplay features are currently active:
- World Info: Shows count of enabled entries (e.g., "WI: 3")
- Persona: Shows persona name when enabled
- Preset: Shows active preset name
- Examples: Shows when message examples are enabled
- Author's Note: Shows when author's note is enabled (displays as "A/N")

Badges update dynamically when features are toggled, providing at-a-glance visibility of active roleplay features without opening the Roleplay Tools panel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-16 22:26:39 -07:00
parent a7c9657ff1
commit 600b50f239
4 changed files with 3797 additions and 0 deletions

3631
CELIA 3.8.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -26,6 +26,9 @@
<div class="avatar-circle"></div> <div class="avatar-circle"></div>
<span id="character-header-name"></span> <span id="character-header-name"></span>
</div> </div>
<div id="feature-badges" class="feature-badges">
<!-- Feature badges will be added here dynamically -->
</div>
<div class="character-controls"> <div class="character-controls">
<div class="select-wrapper"> <div class="select-wrapper">
<select id="character-select" class="character-select"></select> <select id="character-select" class="character-select"></select>

View File

@@ -2082,11 +2082,101 @@ async function loadRoleplaySettings() {
// Load Presets // Load Presets
await loadPresets(); await loadPresets();
// Update feature badges
updateFeatureBadges();
} catch (error) { } catch (error) {
console.error('Failed to load roleplay settings:', error); console.error('Failed to load roleplay settings:', error);
} }
} }
// Update feature badges in header
function updateFeatureBadges() {
const badgesContainer = document.getElementById('feature-badges');
if (!badgesContainer || !currentRoleplaySettings) {
if (badgesContainer) badgesContainer.innerHTML = '';
return;
}
badgesContainer.innerHTML = '';
// World Info badge - show count of enabled entries
const worldInfoEntries = (currentRoleplaySettings.world_info || []).filter(entry => entry.enabled);
if (worldInfoEntries.length > 0) {
const badge = document.createElement('div');
badge.className = 'feature-badge';
badge.title = `${worldInfoEntries.length} World Info ${worldInfoEntries.length === 1 ? 'entry' : 'entries'} active`;
badge.innerHTML = `
<svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
<path d="M2 4h12M2 8h12M2 12h8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<span>WI: <span class="feature-badge-count">${worldInfoEntries.length}</span></span>
`;
badgesContainer.appendChild(badge);
}
// Persona badge
if (currentRoleplaySettings.persona_enabled && currentRoleplaySettings.persona_name) {
const badge = document.createElement('div');
badge.className = 'feature-badge';
badge.title = `Persona: ${currentRoleplaySettings.persona_name}`;
badge.innerHTML = `
<svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
<circle cx="8" cy="5" r="2.5" stroke="currentColor" stroke-width="1.5"/>
<path d="M3 13c0-2.5 2-4 5-4s5 1.5 5 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<span>${currentRoleplaySettings.persona_name}</span>
`;
badgesContainer.appendChild(badge);
}
// Preset badge
const presetSelect = document.getElementById('preset-select');
if (presetSelect && presetSelect.value) {
const presetName = presetSelect.options[presetSelect.selectedIndex]?.text || presetSelect.value;
const badge = document.createElement('div');
badge.className = 'feature-badge';
badge.title = `Active Preset: ${presetName}`;
badge.innerHTML = `
<svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
<rect x="3" y="3" width="10" height="10" rx="1" stroke="currentColor" stroke-width="1.5"/>
<path d="M6 7h4M6 9h4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<span>${presetName}</span>
`;
badgesContainer.appendChild(badge);
}
// Message Examples badge
if (currentRoleplaySettings.examples_enabled) {
const badge = document.createElement('div');
badge.className = 'feature-badge';
badge.title = 'Message Examples enabled';
badge.innerHTML = `
<svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
<path d="M3 6l2 2-2 2M7 10h5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Examples</span>
`;
badgesContainer.appendChild(badge);
}
// Author's Note badge
if (currentRoleplaySettings.authors_note_enabled && currentRoleplaySettings.authors_note) {
const badge = document.createElement('div');
badge.className = 'feature-badge';
badge.title = "Author's Note enabled";
badge.innerHTML = `
<svg class="feature-badge-icon" viewBox="0 0 16 16" fill="none">
<path d="M3 3h10v10H3z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
<path d="M6 6h4M6 9h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<span>A/N</span>
`;
badgesContainer.appendChild(badge);
}
}
// Render World Info entries // Render World Info entries
function renderWorldInfoList(entries) { function renderWorldInfoList(entries) {
const listContainer = document.getElementById('worldinfo-list'); const listContainer = document.getElementById('worldinfo-list');
@@ -2334,6 +2424,9 @@ async function handleToggleWorldInfoEntry(entryId, enabled) {
// Update local settings // Update local settings
entry.enabled = enabled; entry.enabled = enabled;
// Update feature badges
updateFeatureBadges();
} catch (error) { } catch (error) {
console.error('Failed to toggle World Info entry:', error); console.error('Failed to toggle World Info entry:', error);
alert(`Failed to toggle entry: ${error}`); alert(`Failed to toggle entry: ${error}`);
@@ -2372,6 +2465,15 @@ async function handleSaveAuthorsNote() {
enabled enabled
}); });
// Update currentRoleplaySettings
if (currentRoleplaySettings) {
currentRoleplaySettings.authors_note = content;
currentRoleplaySettings.authors_note_enabled = enabled;
}
// Update feature badges
updateFeatureBadges();
// Show success message // Show success message
setStatus('Author\'s Note saved', 'success'); setStatus('Author\'s Note saved', 'success');
setTimeout(() => setStatus('Ready'), 2000); setTimeout(() => setStatus('Ready'), 2000);
@@ -2397,6 +2499,16 @@ async function handleSavePersona() {
enabled enabled
}); });
// Update currentRoleplaySettings
if (currentRoleplaySettings) {
currentRoleplaySettings.persona_name = name;
currentRoleplaySettings.persona_description = description;
currentRoleplaySettings.persona_enabled = enabled;
}
// Update feature badges
updateFeatureBadges();
// Show success message // Show success message
setStatus('Persona saved', 'success'); setStatus('Persona saved', 'success');
setTimeout(() => setStatus('Ready'), 2000); setTimeout(() => setStatus('Ready'), 2000);
@@ -2420,6 +2532,15 @@ async function handleSaveExamples() {
position position
}); });
// Update currentRoleplaySettings
if (currentRoleplaySettings) {
currentRoleplaySettings.examples_enabled = enabled;
currentRoleplaySettings.examples_position = position;
}
// Update feature badges
updateFeatureBadges();
// Show success message // Show success message
setStatus('Message Examples settings saved', 'success'); setStatus('Message Examples settings saved', 'success');
setTimeout(() => setStatus('Ready'), 2000); setTimeout(() => setStatus('Ready'), 2000);
@@ -2605,6 +2726,9 @@ async function handleApplyPreset() {
currentRoleplaySettings.active_preset_id = presetId; currentRoleplaySettings.active_preset_id = presetId;
} }
// Update feature badges
updateFeatureBadges();
setStatus(presetId ? 'Preset applied' : 'Preset removed', 'success'); setStatus(presetId ? 'Preset applied' : 'Preset removed', 'success');
setTimeout(() => setStatus('Ready'), 2000); setTimeout(() => setStatus('Ready'), 2000);
} catch (error) { } catch (error) {

View File

@@ -172,6 +172,45 @@ body {
gap: 8px; gap: 8px;
} }
/* Feature Badges */
.feature-badges {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.feature-badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 8px;
background: rgba(99, 102, 241, 0.1);
border: 1px solid rgba(99, 102, 241, 0.3);
border-radius: 12px;
font-size: 11px;
font-weight: 500;
color: var(--accent);
white-space: nowrap;
transition: all 0.2s ease;
}
.feature-badge:hover {
background: rgba(99, 102, 241, 0.15);
border-color: rgba(99, 102, 241, 0.5);
}
.feature-badge-icon {
width: 10px;
height: 10px;
opacity: 0.8;
}
.feature-badge-count {
font-weight: 600;
color: var(--accent);
}
.icon-btn { .icon-btn {
width: 28px; width: 28px;
height: 28px; height: 28px;