v3.3.0: Fix encounter UI theming and JSON cleaning regex properties
This commit is contained in:
@@ -7,9 +7,12 @@ An immersive RPG extension for browsers that tracks character stats, scene infor
|
||||
|
||||
## 🆕 What's New
|
||||
|
||||
### v3.2.5
|
||||
### v3.3.0
|
||||
|
||||
- Minor bug fixes.
|
||||
- Small upgrades to the combat system.
|
||||
- Regex fix.
|
||||
- Fixed External API logic.
|
||||
- Even more minor bug fixes.
|
||||
|
||||
**Special thanks to all the other contributors for this project:**
|
||||
Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis.
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Marinara",
|
||||
"version": "3.2.5",
|
||||
"version": "3.3.0",
|
||||
"homePage": "https://github.com/SpicyMarinara/rpg-companion-sillytavern"
|
||||
}
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 10px; text-align: center; opacity: 0.6; font-size: 0.85em;">
|
||||
v3.2.5
|
||||
v3.2.6
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting
|
||||
|
||||
if (existingScript) {
|
||||
// Update existing script with new regex pattern if it's different
|
||||
const newPattern = '/```json[\\s\\S]*?```/gim';
|
||||
const newPattern = '/```(?:json|markdown)?[\\s\\S]*?```/gim';
|
||||
|
||||
// Always ensure these properties are set correctly
|
||||
let needsSave = false;
|
||||
@@ -58,18 +58,8 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
if (existingScript.displayOnly !== true) {
|
||||
existingScript.displayOnly = true; // Alter Chat Display
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
if (existingScript.onlyFormatDisplay !== true) {
|
||||
existingScript.onlyFormatDisplay = true; // Alter Chat Display
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
if (existingScript.onlyFormatPrompt !== true) {
|
||||
existingScript.onlyFormatPrompt = true; // Alter Outgoing Prompt
|
||||
if (existingScript.markdownOnly !== true) {
|
||||
existingScript.markdownOnly = true; // Only process markdown
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
@@ -78,11 +68,19 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting
|
||||
needsSave = true;
|
||||
}
|
||||
|
||||
if (existingScript.filterGaslighting !== true) {
|
||||
existingScript.filterGaslighting = true; // Ephemerality
|
||||
if (needsSave && typeof saveSettingsDebounced === 'function') {
|
||||
// Force immediate save and wait for it
|
||||
const saveResult = saveSettingsDebounced();
|
||||
if (saveResult && typeof saveResult.then === 'function') {
|
||||
await saveResult;
|
||||
}
|
||||
// Small delay to ensure save completes
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
console.log('[RPG Companion] ✅ Updated JSON cleaning regex to v3.2.6 settings.');
|
||||
} else {
|
||||
console.log('[RPG Companion] JSON Cleaning Regex is up to date.');
|
||||
}
|
||||
|
||||
console.log('[RPG Companion] JSON Cleaning Regex is already downloaded and active.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,25 +94,22 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting
|
||||
};
|
||||
|
||||
// Create the regex script object for cleaning JSON tracker data
|
||||
// This regex matches ```json...``` code blocks containing tracker data
|
||||
// This regex matches ```json...```, ```markdown...```, or plain ```...``` code blocks
|
||||
// The prompt now explicitly instructs models to use this format
|
||||
// Updated to handle various whitespace scenarios and ensure it catches all variations
|
||||
const regexScript = {
|
||||
id: uuidv4(),
|
||||
scriptName: scriptName,
|
||||
// Match ```json...``` code blocks (handles spaces, newlines, any content)
|
||||
// Match ```json...```, ```markdown...```, or ```...``` code blocks (handles spaces, newlines, any content)
|
||||
// Using a more permissive pattern to catch all variations
|
||||
findRegex: '/```json[\\s\\S]*?```/gim',
|
||||
findRegex: '/```(?:json|markdown)?[\\s\\S]*?```/gim',
|
||||
replaceString: '',
|
||||
trimStrings: [],
|
||||
placement: [2], // 2 = AI Output
|
||||
disabled: false,
|
||||
markdownOnly: false,
|
||||
markdownOnly: true,
|
||||
promptOnly: true, // Enable prompt processing
|
||||
runOnEdit: true,
|
||||
onlyFormatDisplay: true, // Alter Chat Display (enabled)
|
||||
onlyFormatPrompt: true, // Alter Outgoing Prompt (enabled)
|
||||
filterGaslighting: true, // Ephemerality - makes it only affect display
|
||||
substituteRegex: 0,
|
||||
minDepth: null,
|
||||
maxDepth: null
|
||||
|
||||
@@ -51,9 +51,6 @@ export async function generateWithExternalAPI(messages) {
|
||||
if (!baseUrl || !baseUrl.trim()) {
|
||||
throw new Error('External API base URL is not configured');
|
||||
}
|
||||
if (!apiKey || !apiKey.trim()) {
|
||||
throw new Error('External API key is not found. If you switched browsers or cleared your cache, please re-enter your API key in the extension settings.');
|
||||
}
|
||||
if (!model || !model.trim()) {
|
||||
throw new Error('External API model is not configured');
|
||||
}
|
||||
@@ -64,13 +61,19 @@ export async function generateWithExternalAPI(messages) {
|
||||
|
||||
// console.log(`[RPG Companion] Calling external API: ${normalizedBaseUrl} with model: ${model}`);
|
||||
|
||||
// Prepare headers - only include Authorization if API key is provided
|
||||
const headers = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
if (apiKey && apiKey.trim()) {
|
||||
headers['Authorization'] = `Bearer ${apiKey.trim()}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${apiKey.trim()}`
|
||||
},
|
||||
headers: headers,
|
||||
body: JSON.stringify({
|
||||
model: model.trim(),
|
||||
messages: messages,
|
||||
@@ -122,12 +125,10 @@ export async function testExternalAPIConnection() {
|
||||
const { baseUrl, model } = extensionSettings.externalApiSettings || {};
|
||||
const apiKey = localStorage.getItem('rpg_companion_external_api_key');
|
||||
|
||||
if (!baseUrl || !apiKey || !model) {
|
||||
if (!baseUrl || !model) {
|
||||
return {
|
||||
success: false,
|
||||
message: !apiKey
|
||||
? 'API Key not found. Please re-enter it in settings (keys are stored locally per-browser).'
|
||||
: 'Please fill in all required fields (Base URL, API Key, and Model)'
|
||||
message: 'Please fill in all required fields (Base URL and Model). API Key is optional for local servers.'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ export async function buildEncounterInitPrompt() {
|
||||
initInstruction += ` ],\n`;
|
||||
initInstruction += ` "environment": "Brief description of the combat environment",\n`;
|
||||
initInstruction += ` "styleNotes": {\n`;
|
||||
initInstruction += ` "environmentType": "forest|dungeon|desert|cave|city|ruins|snow|water|castle|wasteland|plains|mountains|swamp|volcanic",\n`;
|
||||
initInstruction += ` "environmentType": "forest|dungeon|desert|cave|city|ruins|snow|water|castle|wasteland|plains|mountains|swamp|volcanic|spaceship|mansion",\n`;
|
||||
initInstruction += ` "atmosphere": "bright|dark|foggy|stormy|calm|eerie|chaotic|peaceful",\n`;
|
||||
initInstruction += ` "timeOfDay": "dawn|day|dusk|night|twilight",\n`;
|
||||
initInstruction += ` "weather": "clear|rainy|snowy|windy|stormy|overcast"\n`;
|
||||
@@ -724,9 +724,11 @@ export function parseEncounterJSON(response) {
|
||||
// Remove code blocks if present
|
||||
let cleaned = response.trim();
|
||||
|
||||
// Remove ```json and ``` markers
|
||||
cleaned = cleaned.replace(/```json\s*/gi, '');
|
||||
cleaned = cleaned.replace(/```\s*/g, '');
|
||||
// Remove ```json, ```markdown, and ``` markers (more comprehensive)
|
||||
cleaned = cleaned.replace(/```(?:json|markdown)?\s*/gi, '');
|
||||
|
||||
// Remove any remaining backticks
|
||||
cleaned = cleaned.replace(/`/g, '');
|
||||
|
||||
// Find the first { and last }
|
||||
const firstBrace = cleaned.indexOf('{');
|
||||
|
||||
@@ -155,11 +155,11 @@ export async function onGenerationStarted(type, data, dryRun) {
|
||||
// });
|
||||
}
|
||||
|
||||
// For SEPARATE mode only: Check if we need to commit extension data
|
||||
// For SEPARATE and EXTERNAL modes: Check if we need to commit extension data
|
||||
// BUT: Only do this for the MAIN generation, not the tracker update generation
|
||||
// If isGenerating is true, this is the tracker update generation (second call), so skip flag logic
|
||||
// console.log('[RPG Companion DEBUG] Before generating:', lastGeneratedData.characterThoughts, ' , committed - ', committedTrackerData.characterThoughts);
|
||||
if (extensionSettings.generationMode === 'separate' && !isGenerating) {
|
||||
if ((extensionSettings.generationMode === 'separate' || extensionSettings.generationMode === 'external') && !isGenerating) {
|
||||
if (!lastActionWasSwipe) {
|
||||
// User sent a new message - commit lastGeneratedData before generation
|
||||
// console.log('[RPG Companion] 📝 COMMIT: New message - committing lastGeneratedData');
|
||||
|
||||
@@ -451,8 +451,14 @@ export function renderThoughts() {
|
||||
|
||||
debugLog(`[RPG Thoughts] Looking up avatar for: ${char.name}`);
|
||||
|
||||
// For group chats, search through group members first
|
||||
if (selected_group) {
|
||||
// First, check if user manually uploaded a custom avatar
|
||||
if (extensionSettings.npcAvatars && extensionSettings.npcAvatars[char.name]) {
|
||||
characterPortrait = extensionSettings.npcAvatars[char.name];
|
||||
debugLog('[RPG Thoughts] Found custom uploaded avatar');
|
||||
}
|
||||
|
||||
// For group chats, search through group members
|
||||
if (characterPortrait === FALLBACK_AVATAR_DATA_URI && selected_group) {
|
||||
debugLog('[RPG Thoughts] In group chat, checking group members...');
|
||||
|
||||
try {
|
||||
|
||||
@@ -95,7 +95,7 @@ export class EncounterModal {
|
||||
const combatData = parseEncounterJSON(response);
|
||||
|
||||
if (!combatData || !combatData.party || !combatData.enemies) {
|
||||
this.showErrorWithRegenerate('Invalid JSON format detected. The AI returned malformed data.');
|
||||
this.showErrorWithRegenerate('Invalid JSON format detected. The AI returned malformed data. Ensure the Max Response Length is set to at least 2048 tokens, otherwise the model might run out of tokens and produce unfinished structures.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -417,10 +417,17 @@ export class EncounterModal {
|
||||
const hpPercent = (enemy.hp / enemy.maxHp) * 100;
|
||||
const isDead = enemy.hp <= 0;
|
||||
|
||||
// Try to find avatar for enemy (they might be a character from the chat or Present Characters)
|
||||
const avatarUrl = this.getCharacterAvatar(enemy.name);
|
||||
const sprite = enemy.sprite || '👹';
|
||||
|
||||
// Fallback SVG if no avatar found
|
||||
const fallbackSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2NjY2NjYyIgb3BhY2l0eT0iMC4zIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIiBmaWxsPSIjNjY2IiBmb250LXNpemU9IjQwIj4/PC90ZXh0Pjwvc3ZnPg==';
|
||||
|
||||
return `
|
||||
<div class="rpg-encounter-card ${isDead ? 'rpg-encounter-dead' : ''}" data-enemy-index="${index}">
|
||||
<div class="rpg-encounter-card-sprite">
|
||||
${enemy.sprite || '👹'}
|
||||
${avatarUrl ? `<img src="${avatarUrl}" alt="${enemy.name}" onerror="this.parentElement.innerHTML='${sprite}';this.onerror=null;">` : sprite}
|
||||
</div>
|
||||
<div class="rpg-encounter-card-info">
|
||||
<h4>${enemy.name}</h4>
|
||||
@@ -461,7 +468,7 @@ export class EncounterModal {
|
||||
}
|
||||
} else {
|
||||
// Try to find character avatar by name
|
||||
avatarUrl = this.getPartyMemberAvatar(member.name);
|
||||
avatarUrl = this.getCharacterAvatar(member.name);
|
||||
}
|
||||
|
||||
// Fallback SVG if no avatar found
|
||||
@@ -488,17 +495,31 @@ export class EncounterModal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets avatar for a party member by name
|
||||
* @param {string} name - Party member name
|
||||
* Gets avatar for a character by name (works for party members, enemies, and NPCs)
|
||||
* @param {string} name - Character name
|
||||
* @returns {string} Avatar URL or null
|
||||
*/
|
||||
getPartyMemberAvatar(name) {
|
||||
// Try to get from NPC avatars first
|
||||
getCharacterAvatar(name) {
|
||||
// Priority 1: Check custom uploaded avatars first (from Present Characters panel)
|
||||
if (extensionSettings.npcAvatars && extensionSettings.npcAvatars[name]) {
|
||||
return extensionSettings.npcAvatars[name];
|
||||
}
|
||||
|
||||
// Try to find character by name in loaded characters
|
||||
// Priority 2: Check if character is in the current group
|
||||
if (selected_group) {
|
||||
const groupMembers = getGroupMembers(selected_group);
|
||||
if (groupMembers && groupMembers.length > 0) {
|
||||
const matchingMember = groupMembers.find(member =>
|
||||
member && member.name && member.name.toLowerCase() === name.toLowerCase()
|
||||
);
|
||||
|
||||
if (matchingMember && matchingMember.avatar) {
|
||||
return getSafeThumbnailUrl('avatar', matchingMember.avatar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 3: Search all loaded characters
|
||||
if (characters && Array.isArray(characters)) {
|
||||
const matchingChar = characters.find(char =>
|
||||
char && char.name && char.name.toLowerCase() === name.toLowerCase()
|
||||
@@ -509,7 +530,7 @@ export class EncounterModal {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if it's the current character
|
||||
// Priority 4: Check if it's the current character
|
||||
if (this_chid !== undefined && characters && characters[this_chid]) {
|
||||
const currentChar = characters[this_chid];
|
||||
if (currentChar.name && currentChar.name.toLowerCase() === name.toLowerCase()) {
|
||||
@@ -793,7 +814,7 @@ export class EncounterModal {
|
||||
const result = parseEncounterJSON(response);
|
||||
|
||||
if (!result || !result.combatStats) {
|
||||
this.showErrorWithRegenerate('Invalid JSON format detected. The AI returned malformed data.');
|
||||
this.showErrorWithRegenerate('Invalid JSON format detected. The AI returned malformed data. Ensure the Max Response Length is set to at least 2048 tokens, otherwise the model might run out of tokens and produce unfinished structures.');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1223,76 +1244,8 @@ export class EncounterModal {
|
||||
loadingContent.innerHTML = `
|
||||
<div class="rpg-encounter-error-box">
|
||||
<i class="fa-solid fa-exclamation-triangle" style="color: #e94560; font-size: 48px; margin-bottom: 1em;"></i>
|
||||
<h3 style="color: #e94560; margin: 0 0 0.5em 0;">Wrong Format Detected</h3>
|
||||
<p style="color: #ccc; margin: 0 0 1.5em 0; max-width: 500px;">${message}</p>
|
||||
<div style="display: flex; gap: 1em;">
|
||||
<button id="rpg-error-regenerate" class="rpg-btn rpg-btn-primary">
|
||||
<i class="fa-solid fa-rotate-right"></i> Regenerate
|
||||
</button>
|
||||
<button id="rpg-error-close" class="rpg-btn rpg-btn-secondary">
|
||||
<i class="fa-solid fa-times"></i> Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add event listeners
|
||||
const regenerateBtn = loadingContent.querySelector('#rpg-error-regenerate');
|
||||
const closeBtn = loadingContent.querySelector('#rpg-error-close');
|
||||
|
||||
if (regenerateBtn) {
|
||||
regenerateBtn.addEventListener('click', () => this.regenerateLastRequest());
|
||||
}
|
||||
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('click', () => this.close());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerates the last failed request
|
||||
*/
|
||||
async regenerateLastRequest() {
|
||||
if (!this.lastRequest) {
|
||||
console.warn('[RPG Companion] No request to regenerate');
|
||||
return;
|
||||
}
|
||||
|
||||
// console.log('[RPG Companion] Regenerating request:', this.lastRequest.type);
|
||||
|
||||
if (this.lastRequest.type === 'init') {
|
||||
// Retry initialization
|
||||
this.isInitializing = true;
|
||||
await this.initialize();
|
||||
} else if (this.lastRequest.type === 'action') {
|
||||
// Retry action
|
||||
this.isProcessing = true;
|
||||
await this.processCombatAction(this.lastRequest.action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an error message with a regenerate button
|
||||
* @param {string} message - Error message to display
|
||||
*/
|
||||
showErrorWithRegenerate(message) {
|
||||
const loadingContent = this.modal.querySelector('#rpg-encounter-loading');
|
||||
const combatContent = this.modal.querySelector('#rpg-encounter-content');
|
||||
|
||||
// Hide combat content if visible
|
||||
if (combatContent) {
|
||||
combatContent.style.display = 'none';
|
||||
}
|
||||
|
||||
// Show error in loading area
|
||||
if (loadingContent) {
|
||||
loadingContent.style.display = 'flex';
|
||||
loadingContent.innerHTML = `
|
||||
<div class="rpg-encounter-error-box">
|
||||
<i class="fa-solid fa-exclamation-triangle" style="color: #e94560; font-size: 48px; margin-bottom: 1em;"></i>
|
||||
<h3 style="color: #e94560; margin: 0 0 0.5em 0;">Wrong Format Detected</h3>
|
||||
<p style="color: #ccc; margin: 0 0 1.5em 0; max-width: 500px;">${message}</p>
|
||||
<p style="color: #e94560; font-weight: bold; font-size: 1.2em; margin: 0 0 0.5em 0;">Wrong Format Detected</p>
|
||||
<p style="color: var(--rpg-text, #ccc); margin: 0 0 1.5em 0; max-width: 500px;">${message}</p>
|
||||
<div style="display: flex; gap: 1em;">
|
||||
<button id="rpg-error-regenerate" class="rpg-btn rpg-btn-primary">
|
||||
<i class="fa-solid fa-rotate-right"></i> Regenerate
|
||||
|
||||
@@ -7624,9 +7624,9 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
|
||||
/* Conclude Button */
|
||||
.rpg-encounter-conclude-btn {
|
||||
background: rgba(255, 165, 0, 0.2);
|
||||
border: 1px solid rgba(255, 165, 0, 0.4);
|
||||
color: #ffa500;
|
||||
background: var(--rpg-highlight, rgba(255, 165, 0, 0.2));
|
||||
border: 1px solid var(--rpg-highlight, rgba(255, 165, 0, 0.4));
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
font-size: clamp(12px, 1vw, 14px);
|
||||
font-family: var(--rpg-font-body, 'Open Sans', sans-serif);
|
||||
font-weight: 600;
|
||||
@@ -7641,10 +7641,10 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
}
|
||||
|
||||
.rpg-encounter-conclude-btn:hover {
|
||||
background: rgba(255, 165, 0, 0.3);
|
||||
border-color: rgba(255, 165, 0, 0.6);
|
||||
background: var(--rpg-highlight, rgba(255, 165, 0, 0.3));
|
||||
border-color: var(--rpg-highlight, rgba(255, 165, 0, 0.6));
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(255, 165, 0, 0.3);
|
||||
box-shadow: 0 4px 8px var(--rpg-highlight, rgba(255, 165, 0, 0.3));
|
||||
}
|
||||
|
||||
/* Content Area - Main scrollable container */
|
||||
@@ -7684,19 +7684,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2em;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 2px solid #e94560;
|
||||
background: var(--rpg-bg, rgba(0, 0, 0, 0.5));
|
||||
border: 2px solid var(--rpg-highlight, #e94560);
|
||||
border-radius: 12px;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rpg-encounter-error-box h3 {
|
||||
font-family: var(--rpg-font-heading, 'Cinzel', serif);
|
||||
font-size: 1.5em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.rpg-encounter-error-box p {
|
||||
line-height: 1.6;
|
||||
}
|
||||
@@ -7716,19 +7710,19 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
}
|
||||
|
||||
.rpg-encounter-error-box .rpg-btn-primary {
|
||||
background: #e94560;
|
||||
color: #fff;
|
||||
background: var(--rpg-highlight, #e94560);
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
}
|
||||
|
||||
.rpg-encounter-error-box .rpg-btn-primary:hover {
|
||||
background: #ff5577;
|
||||
background: var(--rpg-highlight-hover, #ff5577);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(233, 69, 96, 0.4);
|
||||
}
|
||||
|
||||
.rpg-encounter-error-box .rpg-btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #ccc;
|
||||
background: var(--rpg-border, rgba(255, 255, 255, 0.1));
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
@@ -7770,6 +7764,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(0.375rem, 0.5vw, 0.5rem);
|
||||
border-left: none !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.rpg-encounter-section h3 i {
|
||||
border-left: none !important;
|
||||
padding-left: 0 !important;
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
}
|
||||
|
||||
.rpg-encounter-section {
|
||||
@@ -7814,6 +7816,18 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
text-align: center;
|
||||
margin-bottom: 0.4vh;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: clamp(40px, 4vw, 60px);
|
||||
}
|
||||
|
||||
.rpg-encounter-card-sprite img {
|
||||
width: clamp(40px, 4vw, 60px);
|
||||
height: clamp(40px, 4vw, 60px);
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 2px solid var(--rpg-border, #4a7ba7);
|
||||
}
|
||||
|
||||
.rpg-encounter-card-avatar {
|
||||
@@ -7888,6 +7902,20 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
/* Combat Log */
|
||||
.rpg-encounter-log-section h3 {
|
||||
margin: 0 0 0.5vh 0;
|
||||
font-size: clamp(14px, 1.2vw, 18px);
|
||||
font-weight: 700;
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(0.375rem, 0.5vw, 0.5rem);
|
||||
border-left: none !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.rpg-encounter-log-section h3 i {
|
||||
border-left: none !important;
|
||||
padding-left: 0 !important;
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
}
|
||||
|
||||
.rpg-encounter-log {
|
||||
@@ -7959,6 +7987,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: clamp(0.375rem, 0.5vw, 0.5rem);
|
||||
border-left: none !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
.rpg-encounter-controls h3 i {
|
||||
border-left: none !important;
|
||||
padding-left: 0 !important;
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
}
|
||||
|
||||
.rpg-encounter-controls h4 {
|
||||
@@ -8272,6 +8308,28 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
border-color: #8b0000;
|
||||
}
|
||||
|
||||
/* Spaceship */
|
||||
.rpg-encounter-modal[data-environment="spaceship"] .rpg-encounter-overlay {
|
||||
background: linear-gradient(135deg, rgba(30, 30, 50, 0.4), rgba(10, 10, 30, 0.6));
|
||||
}
|
||||
|
||||
.rpg-encounter-modal[data-environment="spaceship"] .rpg-encounter-container {
|
||||
background: linear-gradient(to bottom, rgba(50, 60, 80, 0.95), rgba(30, 40, 60, 0.9));
|
||||
border-color: #1e3a5f;
|
||||
box-shadow: 0 0 20px rgba(0, 150, 255, 0.3), inset 0 0 20px rgba(0, 100, 200, 0.1);
|
||||
}
|
||||
|
||||
/* Mansion */
|
||||
.rpg-encounter-modal[data-environment="mansion"] .rpg-encounter-overlay {
|
||||
background: linear-gradient(135deg, rgba(139, 69, 19, 0.3), rgba(101, 67, 33, 0.5));
|
||||
}
|
||||
|
||||
.rpg-encounter-modal[data-environment="mansion"] .rpg-encounter-container {
|
||||
background: linear-gradient(to bottom, rgba(120, 80, 50, 0.95), rgba(90, 60, 40, 0.9));
|
||||
border-color: #654321;
|
||||
box-shadow: 0 0 15px rgba(218, 165, 32, 0.2), inset 0 0 15px rgba(139, 90, 43, 0.1);
|
||||
}
|
||||
|
||||
/* Atmosphere Modifiers */
|
||||
|
||||
/* Dark atmosphere - add shadow overlay */
|
||||
|
||||
Reference in New Issue
Block a user