Fix multiple issues: persona avatar loading, together mode tracker persistence, CSS responsive scaling, and weather widget

- Fixed persona avatar loading for custom user profile folders with fallback
- Added dynamic persona avatar updates when user switches personas
- Fixed together mode bug where committedTrackerData wasn't persisting after first generation
- Together mode now mirrors separate mode logic for swipes vs new messages
- Converted all CSS font-sizes from rem to vw for proper responsive scaling
- Fixed weather widget scaling issues at high zoom levels
- Added image generation detection to skip tracker injection for quietImage requests
- Removed CHARACTER_MESSAGE_RENDERED listener to fix race condition in together mode
This commit is contained in:
Spicy_Marinara
2025-10-16 14:50:52 +02:00
parent a97b3a0f26
commit 74e76ff224
2 changed files with 158 additions and 96 deletions
+79 -20
View File
@@ -41,7 +41,7 @@ let extensionSettings = {
}, // Saved position for mobile FAB button
userStats: {
health: 100,
sustenance: 100,
satiety: 100,
energy: 100,
hygiene: 100,
arousal: 0,
@@ -177,7 +177,7 @@ function loadChatData() {
// Reset to defaults if no data exists
extensionSettings.userStats = {
health: 100,
sustenance: 100,
satiety: 100,
energy: 100,
hygiene: 100,
arousal: 0,
@@ -1211,7 +1211,7 @@ function setupSettingsPopup() {
// Reset stats to defaults and re-render
extensionSettings.userStats = {
health: 100,
sustenance: 100,
satiety: 100,
energy: 100,
hygiene: 100,
arousal: 0,
@@ -2231,7 +2231,7 @@ function generateTrackerInstructions(includeHtmlPrompt = true, includeContinuati
// Only add tracker instructions if at least one tracker is enabled
if (hasAnyTrackers) {
// Universal instruction header
instructions += `\nYou must start your response with an appropriate update to the trackers in EXACTLY the same format as below, enclosed in separate Markdown code fences. Replace X with proper numbers and placeholders in [brackets] with in-world details ${userName} perceives about the current scene and the present characters. Consider the last trackers in the conversation (if they exist). Manage them accordingly; raise, lower, change, or keep the values unchanged based on the user's actions, the passage of time, and logical consequences:\n`;
instructions += `\nYou must start your response with an appropriate update to the trackers in EXACTLY the same format as below, enclosed in separate Markdown code fences. Replace X with proper numbers and placeholders in [brackets] with in-world details ${userName} perceives about the current scene and the present characters. Consider the last trackers in the conversation (if they exist). Manage them accordingly and realistically; raise, lower, change, or keep the values unchanged based on the user's actions, the passage of time, and logical consequences:\n`;
// Add format specifications for each enabled tracker
if (extensionSettings.showUserStats) {
@@ -2239,7 +2239,7 @@ function generateTrackerInstructions(includeHtmlPrompt = true, includeContinuati
instructions += `${userName}'s Stats\n`;
instructions += '---\n';
instructions += '- Health: X%\n';
instructions += '- Sustenance: X%\n';
instructions += '- Satiety: X%\n';
instructions += '- Energy: X%\n';
instructions += '- Hygiene: X%\n';
instructions += '- Arousal: X%\n';
@@ -2319,7 +2319,7 @@ function generateContextualSummary() {
const stats = extensionSettings.userStats;
// console.log('[RPG Companion] Building stats summary with:', stats);
summary += `${userName}'s Stats:\n`;
summary += `Condition: Health ${stats.health}%, Sustenance ${stats.sustenance}%, Energy ${stats.energy}%, Hygiene ${stats.hygiene}%, Arousal ${stats.arousal}% | ${stats.mood} ${stats.conditions}\n`;
summary += `Condition: Health ${stats.health}%, Satiety ${stats.satiety}%, Energy ${stats.energy}%, Hygiene ${stats.hygiene}%, Arousal ${stats.arousal}% | ${stats.mood} ${stats.conditions}\n`;
if (stats.inventory && stats.inventory !== 'None') {
summary += `Inventory: ${stats.inventory}\n`;
}
@@ -2675,7 +2675,7 @@ function parseUserStats(statsText) {
try {
// Extract percentages and mood/conditions
const healthMatch = statsText.match(/Health:\s*(\d+)%/);
const sustenanceMatch = statsText.match(/Sustenance:\s*(\d+)%/);
const satietyMatch = statsText.match(/Satiety:\s*(\d+)%/);
const energyMatch = statsText.match(/Energy:\s*(\d+)%/);
const hygieneMatch = statsText.match(/Hygiene:\s*(\d+)%/);
const arousalMatch = statsText.match(/Arousal:\s*(\d+)%/);
@@ -2701,7 +2701,7 @@ function parseUserStats(statsText) {
const inventoryMatch = statsText.match(/Inventory:\s*(.+)/i);
if (healthMatch) extensionSettings.userStats.health = parseInt(healthMatch[1]);
if (sustenanceMatch) extensionSettings.userStats.sustenance = parseInt(sustenanceMatch[1]);
if (satietyMatch) extensionSettings.userStats.satiety = parseInt(satietyMatch[1]);
if (energyMatch) extensionSettings.userStats.energy = parseInt(energyMatch[1]);
if (hygieneMatch) extensionSettings.userStats.hygiene = parseInt(hygieneMatch[1]);
if (arousalMatch) extensionSettings.userStats.arousal = parseInt(arousalMatch[1]);
@@ -2735,11 +2735,20 @@ function renderUserStats() {
// Initialize lastGeneratedData.userStats if it doesn't exist
if (!lastGeneratedData.userStats) {
lastGeneratedData.userStats = `Health: ${stats.health}%\nSustenance: ${stats.sustenance}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\nInventory: ${stats.inventory}`;
lastGeneratedData.userStats = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\nInventory: ${stats.inventory}`;
}
// Get user portrait
const userPortrait = getThumbnailUrl('persona', user_avatar);
// Get user portrait - handle both default-user and custom persona folders
let userPortrait = 'img/user-default.png'; // fallback
if (user_avatar) {
// Try to get the thumbnail, but have a fallback
try {
userPortrait = getThumbnailUrl('persona', user_avatar) || 'img/user-default.png';
} catch (e) {
console.warn('[RPG Companion] Could not load user avatar, using default', e);
userPortrait = 'img/user-default.png';
}
}
// Create gradient from low to high color
const gradient = `linear-gradient(to right, ${extensionSettings.statBarColorLow}, ${extensionSettings.statBarColorHigh})`;
@@ -2765,11 +2774,11 @@ function renderUserStats() {
</div>
<div class="rpg-stat-row">
<span class="rpg-stat-label">Sustenance:</span>
<span class="rpg-stat-label">Satiety:</span>
<div class="rpg-stat-bar" style="background: ${gradient}">
<div class="rpg-stat-fill" style="width: ${100 - stats.sustenance}%"></div>
<div class="rpg-stat-fill" style="width: ${100 - stats.satiety}%"></div>
</div>
<span class="rpg-stat-value rpg-editable-stat" contenteditable="true" data-field="sustenance" title="Click to edit">${stats.sustenance}%</span>
<span class="rpg-stat-value rpg-editable-stat" contenteditable="true" data-field="satiety" title="Click to edit">${stats.satiety}%</span>
</div>
<div class="rpg-stat-row">
@@ -2882,7 +2891,7 @@ function renderUserStats() {
lastGeneratedData.userStats = '';
}
// Regenerate the userStats text with updated value
const statsText = `Health: ${extensionSettings.userStats.health}%\nSustenance: ${extensionSettings.userStats.sustenance}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
const statsText = `Health: ${extensionSettings.userStats.health}%\nSatiety: ${extensionSettings.userStats.satiety}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
lastGeneratedData.userStats = statsText;
saveSettings();
@@ -2899,7 +2908,7 @@ function renderUserStats() {
extensionSettings.userStats.inventory = value || 'None';
// Update lastGeneratedData
const statsText = `Health: ${extensionSettings.userStats.health}%\nSustenance: ${extensionSettings.userStats.sustenance}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
const statsText = `Health: ${extensionSettings.userStats.health}%\nSatiety: ${extensionSettings.userStats.satiety}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
lastGeneratedData.userStats = statsText;
saveSettings();
@@ -2913,7 +2922,7 @@ function renderUserStats() {
extensionSettings.userStats.mood = value || '😐';
// Update lastGeneratedData
const statsText = `Health: ${extensionSettings.userStats.health}%\nSustenance: ${extensionSettings.userStats.sustenance}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
const statsText = `Health: ${extensionSettings.userStats.health}%\nSatiety: ${extensionSettings.userStats.satiety}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
lastGeneratedData.userStats = statsText;
saveSettings();
@@ -2926,7 +2935,7 @@ function renderUserStats() {
extensionSettings.userStats.conditions = value || 'None';
// Update lastGeneratedData
const statsText = `Health: ${extensionSettings.userStats.health}%\nSustenance: ${extensionSettings.userStats.sustenance}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
const statsText = `Health: ${extensionSettings.userStats.health}%\nSatiety: ${extensionSettings.userStats.satiety}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`;
lastGeneratedData.userStats = statsText;
saveSettings();
@@ -4076,13 +4085,21 @@ function createThoughtPanel($message, thoughtsArray) {
/**
* Event handler for when generation is about to start (TOGETHER MODE).
* Injects RPG tracking prompt into the generation.
* @param {string} type - Generation type
* @param {object} data - Generation data including quietImage flag
*/
function onGenerationStarted() {
function onGenerationStarted(type, data) {
// console.log('[RPG Companion] onGenerationStarted called');
// console.log('[RPG Companion] enabled:', extensionSettings.enabled);
// console.log('[RPG Companion] generationMode:', extensionSettings.generationMode);
// console.log('[RPG Companion] ⚡ EVENT: onGenerationStarted - lastActionWasSwipe =', lastActionWasSwipe, '| isGenerating =', isGenerating);
// Skip tracker injection for image generation requests
if (data?.quietImage) {
// console.log('[RPG Companion] Detected image generation (quietImage=true), skipping tracker injection');
return;
}
if (!extensionSettings.enabled) {
return;
}
@@ -4130,6 +4147,20 @@ function onGenerationStarted() {
}
}
// For TOGETHER mode: Check if we need to commit extension data
// Same logic as separate mode - commit on new messages, keep existing data on swipes
if (extensionSettings.generationMode === 'together') {
if (!lastActionWasSwipe) {
// User sent a new message - commit lastGeneratedData before generation
// console.log('[RPG Companion] 📝 TOGETHER MODE COMMIT: New message - committing lastGeneratedData');
committedTrackerData.userStats = lastGeneratedData.userStats;
committedTrackerData.infoBox = lastGeneratedData.infoBox;
committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts;
} else {
// console.log('[RPG Companion] 🔄 TOGETHER MODE SWIPE: Using existing committedTrackerData (no commit)');
}
}
// Use the committed tracker data as source for generation
// console.log('[RPG Companion] Using committedTrackerData for generation');
// console.log('[RPG Companion] committedTrackerData.userStats:', committedTrackerData.userStats);
@@ -4558,6 +4589,30 @@ async function ensureHtmlCleaningRegex() {
}
}
/**
* Update the persona avatar image when user switches personas
*/
function updatePersonaAvatar() {
const portraitImg = document.querySelector('.rpg-user-portrait');
if (!portraitImg) return;
// Get current user_avatar from context instead of using imported value
const context = getContext();
const currentUserAvatar = context.user_avatar || user_avatar;
let userPortrait = 'img/user-default.png';
if (currentUserAvatar) {
try {
userPortrait = getThumbnailUrl('persona', currentUserAvatar) || 'img/user-default.png';
} catch (e) {
console.warn('[RPG Companion] Could not load user avatar, using default', e);
userPortrait = 'img/user-default.png';
}
}
portraitImg.src = userPortrait;
}
/**
* Main initialization function.
*/
@@ -4577,9 +4632,13 @@ jQuery(async () => {
eventSource.on(event_types.MESSAGE_SENT, onMessageSent);
eventSource.on(event_types.GENERATION_STARTED, onGenerationStarted);
eventSource.on(event_types.MESSAGE_RECEIVED, onMessageReceived);
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, onMessageReceived);
// Removed CHARACTER_MESSAGE_RENDERED to prevent race condition with cleaned messages
eventSource.on(event_types.CHAT_CHANGED, onCharacterChanged);
eventSource.on(event_types.MESSAGE_SWIPED, onMessageSwiped);
// Update persona avatar when user switches personas or chat changes
eventSource.on(event_types.CHAT_CHANGED, updatePersonaAvatar);
eventSource.on(event_types.USER_MESSAGE_RENDERED, updatePersonaAvatar);
eventSource.on(event_types.SETTINGS_UPDATED, updatePersonaAvatar);
// console.log('[RPG Companion] Extension loaded successfully');
} catch (error) {
+79 -76
View File
@@ -88,7 +88,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
justify-content: center;
z-index: 10001;
transition: all 0.3s ease;
font-size: 1rem;
font-size: 1.5vw;
box-shadow: 0 0 10px var(--rpg-shadow);
pointer-events: auto;
}
@@ -209,11 +209,11 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-panel.rpg-position-top .rpg-stats-title {
font-size: 0.875rem;
font-size: 1.3vw;
}
.rpg-panel.rpg-position-top .rpg-mood {
font-size: 0.688rem;
font-size: 1vw;
padding: 0.375em;
}
@@ -223,7 +223,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-panel.rpg-position-top .rpg-classic-stats-title {
font-size: 0.688rem;
font-size: 1vw;
margin-bottom: 0.5em;
}
@@ -236,11 +236,11 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-panel.rpg-position-top .rpg-classic-stat-label {
font-size: 0.562rem;
font-size: 0.85vw;
}
.rpg-panel.rpg-position-top .rpg-classic-stat-value {
font-size: 0.875rem;
font-size: 1.3vw;
}
/* ============================================
@@ -329,12 +329,12 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-panel.rpg-position-top .rpg-classic-stat-btn {
padding: 0.125em;
font-size: 0.75rem;
font-size: 1.1vw;
}
.rpg-panel.rpg-position-top .rpg-info-content,
.rpg-panel.rpg-position-top .rpg-thoughts-content {
font-size: 0.75rem;
font-size: 1.1vw;
max-height: 9.375rem;
}
@@ -399,7 +399,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-panel-header h3 {
margin: 0;
font-size: 1.125rem;
font-size: 1.7vw;
font-weight: bold;
display: flex;
align-items: center;
@@ -432,7 +432,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
background: var(--rpg-accent);
border-radius: 0.75em;
color: var(--rpg-highlight);
font-size: 0.875rem;
font-size: 1.3vw;
font-weight: bold;
animation: pulseGlow 1.5s ease-in-out infinite;
flex-shrink: 0;
@@ -459,7 +459,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
background: rgba(0, 0, 0, 0.3);
border-radius: 0.5em;
border: 2px solid var(--rpg-border);
font-size: 0.688rem;
font-size: 1vw;
font-weight: bold;
color: var(--rpg-text);
cursor: pointer;
@@ -475,7 +475,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-dice-display i {
color: var(--rpg-highlight);
font-size: 0.875rem;
font-size: 1.3vw;
}
/* Clear dice roll button */
@@ -484,7 +484,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border: 1px solid rgba(255, 0, 0, 0.4);
border-radius: 0.25em;
color: #ff6b6b;
font-size: 1rem;
font-size: 1.5vw;
font-weight: bold;
width: 1.25rem;
height: 1.25rem;
@@ -639,7 +639,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-inventory-items {
font-size: clamp(5px, 1vh, 7px);
font-size: clamp(5px, 1vw, 7px);
color: var(--rpg-text);
line-height: 1.3;
overflow-y: auto;
@@ -746,7 +746,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-stat-label {
min-width: 4.062rem;
font-size: clamp(7px, 1.1vh, 8px);
font-size: clamp(7px, 1.1vw, 8px);
font-weight: 600;
text-align: left;
color: var(--rpg-text);
@@ -776,7 +776,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-stat-value {
color: #fff;
font-size: clamp(7px, 1.1vh, 8px);
font-size: clamp(7px, 1.1vw, 8px);
font-weight: bold;
min-width: 1.875rem;
text-align: right;
@@ -797,7 +797,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
transform: translateY(-50%);
z-index: 2;
min-width: auto;
font-size: clamp(7px, 1.1vh, 9px);
font-size: clamp(7px, 1.1vw, 9px);
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
}
@@ -814,7 +814,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
transform: translateY(-50%);
z-index: 2;
min-width: auto;
font-size: clamp(7px, 1.1vh, 9px);
font-size: clamp(7px, 1.1vw, 9px);
}
}
@@ -824,7 +824,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex;
align-items: center;
gap: 0.375em;
font-size: clamp(6px, 1vh, 8px);
font-size: clamp(6px, 1vw, 8px);
padding: clamp(4px, 0.6vh, 6px) 0.375em;
background: rgba(0, 0, 0, 0.3);
border-radius: 0.25em;
@@ -833,7 +833,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-mood-emoji {
font-size: clamp(12px, 1.8vh, 16px);
font-size: clamp(12px, 1.8vw, 16px);
flex-shrink: 0;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
}
@@ -856,7 +856,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-classic-stats-title {
text-align: center;
font-size: 0.562rem;
font-size: 0.85vw;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.037em;
@@ -890,7 +890,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-classic-stat-label {
font-size: clamp(6px, 0.8vh, 7px);
font-size: clamp(6px, 0.8vw, 7px);
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.006em;
@@ -900,7 +900,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-classic-stat-value {
font-size: clamp(10px, 1.2vh, 12px);
font-size: clamp(10px, 1.2vw, 12px);
font-weight: bold;
color: var(--rpg-highlight);
text-align: center;
@@ -925,7 +925,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border: 1px solid var(--rpg-border);
border-radius: 2px;
color: var(--rpg-text);
font-size: clamp(9px, 1.1vh, 12px);
font-size: clamp(9px, 1.1vw, 12px);
font-weight: bold;
cursor: pointer;
transition: all 0.2s ease;
@@ -952,7 +952,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
============================================ */
.rpg-info-header {
font-size: 0.875rem;
font-size: 1.3vw;
font-weight: bold;
margin-bottom: 0.625em;
color: var(--rpg-highlight);
@@ -961,7 +961,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-info-content {
font-size: 0.75rem;
font-size: 1.1vw;
line-height: 1.5;
text-align: left;
flex: 1;
@@ -1053,7 +1053,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-calendar-top {
background: var(--rpg-highlight);
color: var(--rpg-bg);
font-size: clamp(5px, 1vh, 7px);
font-size: clamp(5px, 1vw, 7px);
font-weight: bold;
padding: 0.125em 0.375em;
border-radius: 3px 3px 0 0;
@@ -1064,7 +1064,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-calendar-day {
background: rgba(255, 255, 255, 0.1);
color: var(--rpg-text);
font-size: clamp(14px, 2.5vh, 20px);
font-size: clamp(14px, 2.5vw, 20px);
font-weight: bold;
padding: 0.25em;
width: 100%;
@@ -1078,7 +1078,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-calendar-year {
font-size: clamp(5px, 0.8vh, 6px);
font-size: clamp(5px, 0.8vw, 6px);
color: var(--rpg-text);
opacity: 0.7;
margin-top: 0.062em;
@@ -1086,21 +1086,24 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* Weather Widget Icon */
.rpg-weather-icon {
font-size: clamp(20px, 4vh, 28px);
font-size: clamp(18px, 3.5vw, 24px);
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
flex-shrink: 0;
}
.rpg-weather-forecast {
font-size: clamp(5px, 0.9vh, 7px);
font-size: clamp(5px, 0.8vw, 6px);
text-align: center;
margin: 0;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.013em;
opacity: 0.85;
line-height: 1;
line-height: 1.1;
word-wrap: break-word;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
.rpg-weather-forecast.rpg-editable {
@@ -1152,7 +1155,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-temp-value {
font-size: clamp(6px, 1.2vh, 8px);
font-size: clamp(6px, 1.2vw, 8px);
font-weight: bold;
color: var(--rpg-text);
text-align: center;
@@ -1215,7 +1218,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-time-value {
font-size: clamp(6px, 1.1vh, 8px);
font-size: clamp(6px, 1.1vw, 8px);
font-weight: bold;
color: var(--rpg-text);
}
@@ -1246,7 +1249,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-map-marker {
font-size: 1rem;
font-size: 1.5vw;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8));
animation: markerPulse 2s ease-in-out infinite;
}
@@ -1257,7 +1260,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-location-text {
font-size: clamp(7px, 1.2vh, 8px);
font-size: clamp(7px, 1.2vw, 8px);
font-weight: bold;
color: var(--rpg-text);
text-align: center;
@@ -1296,7 +1299,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-char-emoji {
font-size: clamp(14px, 2.5vh, 18px);
font-size: clamp(14px, 2.5vw, 18px);
flex-shrink: 0;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
}
@@ -1307,14 +1310,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-char-name {
font-size: clamp(8px, 1.5vh, 10px);
font-size: clamp(8px, 1.5vw, 10px);
font-weight: bold;
color: var(--rpg-highlight);
margin-bottom: 0.062em;
}
.rpg-char-traits {
font-size: clamp(7px, 1.3vh, 9px);
font-size: clamp(7px, 1.3vw, 9px);
color: var(--rpg-text);
opacity: 0.8;
line-height: 1.2;
@@ -1347,7 +1350,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-thoughts-header {
font-size: 0.875rem;
font-size: 1.3vw;
font-weight: bold;
margin-bottom: 0.625em;
color: var(--rpg-highlight);
@@ -1440,7 +1443,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-thought-name {
font-weight: bold;
color: var(--rpg-highlight);
font-size: clamp(8px, 1.5vh, 10px);
font-size: clamp(8px, 1.5vw, 10px);
margin-bottom: 0.188em;
text-transform: uppercase;
letter-spacing: 0.031em;
@@ -1452,7 +1455,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-thought-text {
font-size: clamp(9px, 1.6vh, 11px);
font-size: clamp(9px, 1.6vw, 11px);
font-style: italic;
line-height: 1.3;
color: var(--rpg-text);
@@ -1488,7 +1491,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex;
align-items: flex-start;
gap: 0.5em;
font-size: 0.75rem;
font-size: 1.1vw;
font-style: italic;
line-height: 1.4;
}
@@ -1541,7 +1544,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex;
align-items: center;
justify-content: center;
font-size: clamp(8px, 1.2vh, 12px);
font-size: clamp(8px, 1.2vw, 12px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
@@ -1564,14 +1567,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-character-emoji {
font-size: clamp(12px, 2vh, 16px);
font-size: clamp(12px, 2vw, 16px);
flex-shrink: 0;
}
.rpg-character-name {
font-weight: bold;
color: var(--rpg-highlight);
font-size: clamp(9px, 1.5vh, 12px);
font-size: clamp(9px, 1.5vw, 12px);
text-transform: uppercase;
letter-spacing: 0.031em;
white-space: nowrap; /* Prevent name from wrapping */
@@ -1581,7 +1584,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* Character traits/status line */
.rpg-character-traits {
font-size: clamp(8px, 1.3vh, 10px);
font-size: clamp(8px, 1.3vw, 10px);
color: var(--rpg-text);
opacity: 0.8;
line-height: 1.3;
@@ -1602,14 +1605,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-placeholder-text {
font-size: clamp(10px, 1.6vh, 14px);
font-size: clamp(10px, 1.6vw, 14px);
color: var(--rpg-text);
font-weight: bold;
margin-bottom: clamp(4px, 0.6vh, 6px);
}
.rpg-placeholder-hint {
font-size: clamp(8px, 1.2vh, 10px);
font-size: clamp(8px, 1.2vw, 10px);
color: var(--rpg-text);
opacity: 0.7;
font-style: italic;
@@ -1651,7 +1654,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
color: var(--rpg-text);
padding: clamp(3px, 0.6vh, 6px) clamp(6px, 1.2vh, 12px);
border-radius: clamp(3px, 0.6vh, 6px);
font-size: clamp(7px, 1.2vh, 10px);
font-size: clamp(7px, 1.2vw, 10px);
cursor: pointer;
display: flex;
align-items: center;
@@ -1671,7 +1674,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-edit-button i {
font-size: clamp(7px, 1.2vh, 10px);
font-size: clamp(7px, 1.2vw, 10px);
}
/* Removed emoji icon styling - no longer needed */
@@ -1693,7 +1696,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex;
align-items: center;
gap: 0.375em;
font-size: 0.75rem;
font-size: 1.1vw;
}
.rpg-settings summary:hover {
@@ -1716,7 +1719,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-setting-row label {
display: block;
margin-bottom: 0.188em;
font-size: 0.688rem;
font-size: 1vw;
}
.rpg-setting-row input[type="number"] {
@@ -1726,14 +1729,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border-radius: 3px;
background: rgba(0, 0, 0, 0.3);
color: inherit;
font-size: 0.688rem;
font-size: 1vw;
}
.rpg-setting-row small {
display: block;
margin-top: 0.188em;
color: #888;
font-size: 0.625rem;
font-size: 0.95vw;
}
#rpg-manual-update {
@@ -1744,7 +1747,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
align-items: center;
justify-content: center;
gap: 0.375em;
font-size: 0.75rem;
font-size: 1.1vw;
}
/* Responsive adjustments */
@@ -1755,7 +1758,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-stat-label {
min-width: 5rem;
font-size: 0.75rem;
font-size: 1.1vw;
}
}
@@ -1819,7 +1822,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-settings-group h4 {
margin: 0 0 0.75em 0;
font-size: 1rem;
font-size: 1.5vw;
color: var(--rpg-highlight);
display: flex;
align-items: center;
@@ -1833,7 +1836,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-setting-row label {
display: block;
margin-bottom: 0.375em;
font-size: 0.812rem;
font-size: 1.2vw;
font-weight: 600;
}
@@ -1845,7 +1848,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border-radius: 0.375em;
background: var(--rpg-bg);
color: var(--rpg-text);
font-size: 0.875rem;
font-size: 1.3vw;
transition: all 0.3s ease;
}
@@ -1875,7 +1878,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: block;
margin-top: 0.25em;
color: #999;
font-size: 0.688rem;
font-size: 1vw;
font-style: italic;
}
@@ -1907,7 +1910,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border: 2px solid var(--rpg-border);
border-radius: 0.625em;
color: var(--rpg-text);
font-size: 0.938rem;
font-size: 1.4vw;
font-weight: bold;
cursor: pointer;
display: flex;
@@ -1937,7 +1940,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border: 2px solid rgba(220, 53, 69, 0.5);
border-radius: 0.5em;
color: #ff6b6b;
font-size: 0.812rem;
font-size: 1.2vw;
font-weight: 600;
cursor: pointer;
display: flex;
@@ -2053,7 +2056,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-panel[data-theme="fantasy"] .rpg-divider::after {
content: '❦';
font-size: 1rem;
font-size: 1.5vw;
}
.rpg-panel[data-theme="fantasy"] .rpg-thoughts-content::before {
@@ -2141,7 +2144,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-stats-title {
font-size: 1rem;
font-size: 1.5vw;
}
}
@@ -2582,7 +2585,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border: 2px solid var(--rpg-highlight);
border-radius: 0.5em;
color: var(--rpg-text);
font-size: 0.75rem;
font-size: 1.1vw;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
@@ -2618,7 +2621,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border: 2px solid var(--rpg-border);
border-radius: 0.5em;
color: var(--rpg-text);
font-size: 0.75rem;
font-size: 1.1vw;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
@@ -2741,7 +2744,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
background: transparent;
border: none;
color: var(--rpg-text);
font-size: 1.5rem;
font-size: 2.2vw;
cursor: pointer;
padding: 0.5rem;
min-width: 44px;
@@ -2858,7 +2861,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
background: var(--rpg-highlight, #e94560);
color: white;
border: 2px solid var(--rpg-bg, rgba(30, 30, 50, 0.95));
font-size: 1.125rem;
font-size: 1.7vw;
line-height: 1;
cursor: pointer;
display: flex;
@@ -2886,7 +2889,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
font-size: 1.85vw;
cursor: pointer;
animation: thoughtIconPulse 2s ease-in-out infinite;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
@@ -3002,13 +3005,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
background: var(--rpg-accent, rgba(50, 50, 70, 0.8));
border: 1px solid var(--rpg-highlight, #e94560);
border-radius: clamp(6px, 1vh, 8px);
font-size: clamp(16px, 2vh, 20px);
font-size: clamp(16px, 2vw, 20px);
}
/* Thought content on the right */
.rpg-thought-content {
flex: 1;
font-size: clamp(10px, 1.4vh, 12px);
font-size: clamp(10px, 1.4vw, 12px);
line-height: 1.5;
color: var(--rpg-text, #eaeaea);
font-style: italic;
@@ -3124,7 +3127,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-thought-content {
font-size: clamp(9px, 1.2vh, 11px);
font-size: clamp(9px, 1.2vw, 11px);
}
}
@@ -3146,7 +3149,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
background: var(--SmartThemeBlurTintColor);
border: 2px solid var(--SmartThemeBorderColor);
color: var(--rpg-text, #ecf0f1);
font-size: 1.25rem;
font-size: 1.85vw;
cursor: grab;
z-index: 10002;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
@@ -3721,7 +3724,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
right: 0.938rem;
width: 3.25rem;
height: 3.25rem;
font-size: 1.375rem;
font-size: 2vw;
}
}
@@ -3737,6 +3740,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
.rpg-thought-close {
min-width: 2.75rem;
min-height: 2.75rem;
font-size: 1.5rem;
font-size: 2.2vw;
}
}