@@ -43,10 +84,10 @@ export function renderInfoBox() {
return;
}
- // console.log('[RPG Companion] renderInfoBox called with data:', lastGeneratedData.infoBox);
+ // console.log('[RPG Companion] renderInfoBox called with data:', infoBoxData);
// Parse the info box data
- const lines = lastGeneratedData.infoBox.split('\n');
+ const lines = infoBoxData.split('\n');
// console.log('[RPG Companion] Info Box split into lines:', lines);
const data = {
date: '',
@@ -155,11 +196,24 @@ export function renderInfoBox() {
}
} else if (line.startsWith('Weather:')) {
if (!parsedFields.weather) {
- // New text format: Weather: [Emoji], [Forecast]
+ // New text format: Weather: [Emoji], [Forecast] OR Weather: [Emoji][Forecast] (no separator - FIXED)
const weatherStr = line.replace('Weather:', '').trim();
- const weatherParts = weatherStr.split(',').map(p => p.trim());
- data.weatherEmoji = weatherParts[0] || '';
- data.weatherForecast = weatherParts[1] || '';
+ const { emoji, text } = separateEmojiFromText(weatherStr);
+
+ if (emoji && text) {
+ data.weatherEmoji = emoji;
+ data.weatherForecast = text;
+ } else if (weatherStr.includes(',')) {
+ // Fallback to comma split if emoji detection failed
+ const weatherParts = weatherStr.split(',').map(p => p.trim());
+ data.weatherEmoji = weatherParts[0] || '';
+ data.weatherForecast = weatherParts[1] || '';
+ } else {
+ // No clear separation - assume it's all forecast text
+ data.weatherEmoji = '🌤️'; // Default emoji
+ data.weatherForecast = weatherStr;
+ }
+
parsedFields.weather = true;
}
} else {
@@ -217,8 +271,11 @@ export function renderInfoBox() {
// });
// Build visual dashboard HTML
+ // Wrap all content in a scrollable container
+ let html = '
';
+
// Row 1: Date, Weather, Temperature, Time widgets
- let html = '
';
+ html += '
';
// Calendar widget - always show (editable even if empty)
// Display abbreviated version but allow editing full value
@@ -301,6 +358,67 @@ export function renderInfoBox() {
`;
+ // Row 3: Recent Events widget (notebook style) - dynamically show 1-3 events
+ // Parse Recent Events from infoBox string
+ let recentEvents = [];
+ if (committedTrackerData.infoBox) {
+ const recentEventsLine = committedTrackerData.infoBox.split('\n').find(line => line.startsWith('Recent Events:'));
+ if (recentEventsLine) {
+ const eventsString = recentEventsLine.replace('Recent Events:', '').trim();
+ if (eventsString) {
+ recentEvents = eventsString.split(',').map(e => e.trim()).filter(e => e);
+ }
+ }
+ }
+
+ const validEvents = recentEvents.filter(e => e && e.trim() && e !== 'Event 1' && e !== 'Event 2' && e !== 'Event 3');
+
+ // If no valid events, show at least one placeholder
+ if (validEvents.length === 0) {
+ validEvents.push('Click to add event');
+ }
+
+ html += `
+
+ `;
+
+ // Close the scrollable content wrapper
+ html += '
';
+
$infoBoxContainer.html(html);
// Add event handlers for editable Info Box fields
@@ -320,7 +438,12 @@ export function renderInfoBox() {
}
}
- updateInfoBoxField(field, value);
+ // Handle recent events separately
+ if (field === 'event1' || field === 'event2' || field === 'event3') {
+ updateRecentEvent(field, value);
+ } else {
+ updateInfoBoxField(field, value);
+ }
});
// For date fields, show full value on focus
@@ -610,3 +733,84 @@ export function updateInfoBoxField(field, value) {
renderInfoBox();
}
}
+
+/**
+ * Update a recent event in the committed tracker data
+ * @param {string} field - event1, event2, or event3
+ * @param {string} value - New event text
+ */
+function updateRecentEvent(field, value) {
+ // Map field to index
+ const eventIndex = {
+ 'event1': 0,
+ 'event2': 1,
+ 'event3': 2
+ }[field];
+
+ if (eventIndex !== undefined) {
+ // Parse current infoBox to get existing events
+ const lines = (committedTrackerData.infoBox || '').split('\n');
+ let recentEvents = [];
+
+ // Find existing Recent Events line
+ const recentEventsLine = lines.find(line => line.startsWith('Recent Events:'));
+ if (recentEventsLine) {
+ const eventsString = recentEventsLine.replace('Recent Events:', '').trim();
+ if (eventsString) {
+ recentEvents = eventsString.split(',').map(e => e.trim()).filter(e => e);
+ }
+ }
+
+ // Ensure array has enough slots
+ while (recentEvents.length <= eventIndex) {
+ recentEvents.push('');
+ }
+
+ // Update the specific event
+ recentEvents[eventIndex] = value;
+
+ // Filter out empty events and rebuild the line
+ const validEvents = recentEvents.filter(e => e && e.trim());
+ const newRecentEventsLine = validEvents.length > 0
+ ? `Recent Events: ${validEvents.join(', ')}`
+ : '';
+
+ // Update infoBox with new Recent Events line
+ const updatedLines = lines.filter(line => !line.startsWith('Recent Events:'));
+ if (newRecentEventsLine) {
+ // Add Recent Events line at the end (before any empty lines)
+ let insertIndex = updatedLines.length;
+ for (let i = updatedLines.length - 1; i >= 0; i--) {
+ if (updatedLines[i].trim() !== '') {
+ insertIndex = i + 1;
+ break;
+ }
+ }
+ updatedLines.splice(insertIndex, 0, newRecentEventsLine);
+ }
+
+ committedTrackerData.infoBox = updatedLines.join('\n');
+ lastGeneratedData.infoBox = updatedLines.join('\n');
+
+ // Update the message's swipe data
+ const chat = getContext().chat;
+ if (chat && chat.length > 0) {
+ for (let i = chat.length - 1; i >= 0; i--) {
+ const message = chat[i];
+ if (!message.is_user) {
+ if (message.extra && message.extra.rpg_companion_swipes) {
+ const swipeId = message.swipe_id || 0;
+ if (message.extra.rpg_companion_swipes[swipeId]) {
+ message.extra.rpg_companion_swipes[swipeId].infoBox = updatedLines.join('\n');
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ saveChatData();
+ renderInfoBox();
+ console.log(`[RPG Companion] Updated recent event ${field}:`, value);
+ }
+}
diff --git a/src/systems/rendering/quests.js b/src/systems/rendering/quests.js
new file mode 100644
index 0000000..efaa8da
--- /dev/null
+++ b/src/systems/rendering/quests.js
@@ -0,0 +1,306 @@
+/**
+ * Quests Rendering Module
+ * Handles UI rendering for quests system (main and optional quests)
+ */
+
+import { extensionSettings, $questsContainer } from '../../core/state.js';
+import { saveSettings } from '../../core/persistence.js';
+
+/**
+ * HTML escape helper
+ * @param {string} text - Text to escape
+ * @returns {string} Escaped HTML
+ */
+function escapeHtml(text) {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+}
+
+/**
+ * Renders the quests sub-tab navigation (Main, Optional)
+ * @param {string} activeTab - Currently active sub-tab ('main', 'optional')
+ * @returns {string} HTML for sub-tab navigation
+ */
+export function renderQuestsSubTabs(activeTab = 'main') {
+ return `
+
+
+
+
+ `;
+}
+
+/**
+ * Renders the main quest view
+ * @param {string} mainQuest - Current main quest title
+ * @returns {string} HTML for main quest view
+ */
+export function renderMainQuestView(mainQuest) {
+ const questDisplay = (mainQuest && mainQuest !== 'None') ? mainQuest : '';
+ const hasQuest = questDisplay.length > 0;
+
+ return `
+
+
+
+ ${hasQuest ? `
+
+
+
${escapeHtml(questDisplay)}
+
+
+
+
+
+ ` : `
+
+
No active main quests
+ `}
+
+
+
+ The main quests represent your primary objective in the story.
+
+
+ `;
+}
+
+/**
+ * Renders the optional quests view
+ * @param {string[]} optionalQuests - Array of optional quest titles
+ * @returns {string} HTML for optional quests view
+ */
+export function renderOptionalQuestsView(optionalQuests) {
+ const quests = optionalQuests.filter(q => q && q !== 'None');
+
+ let questsHtml = '';
+ if (quests.length === 0) {
+ questsHtml = '
No active optional quests
';
+ } else {
+ questsHtml = quests.map((quest, index) => `
+
+
${escapeHtml(quest)}
+
+
+
+
+ `).join('');
+ }
+
+ return `
+
+
+
+
+
+ ${questsHtml}
+
+
+
+ Optional quests are side objectives that complement your main story.
+
+
+
+ `;
+}
+
+/**
+ * Main render function for quests
+ */
+export function renderQuests() {
+ if (!extensionSettings.showInventory || !$questsContainer) {
+ return;
+ }
+
+ // Get current sub-tab from container or default to 'main'
+ const activeSubTab = $questsContainer.data('active-subtab') || 'main';
+
+ // Get quests data
+ const mainQuest = extensionSettings.quests.main || 'None';
+ const optionalQuests = extensionSettings.quests.optional || [];
+
+ // Build HTML
+ let html = '
';
+ html += renderQuestsSubTabs(activeSubTab);
+
+ // Render active sub-tab
+ html += '
';
+ if (activeSubTab === 'main') {
+ html += renderMainQuestView(mainQuest);
+ } else {
+ html += renderOptionalQuestsView(optionalQuests);
+ }
+ html += '
';
+
+ $questsContainer.html(html);
+
+ // Attach event handlers
+ attachQuestEventHandlers();
+}
+
+/**
+ * Attach event handlers for quest interactions
+ */
+function attachQuestEventHandlers() {
+ // Sub-tab switching
+ $questsContainer.find('.rpg-quests-subtab').on('click', function() {
+ const tab = $(this).data('tab');
+ $questsContainer.data('active-subtab', tab);
+ renderQuests();
+ });
+
+ // Add quest button
+ $questsContainer.find('[data-action="add-quest"]').on('click', function() {
+ const field = $(this).data('field');
+ $(`#rpg-add-quest-form-${field}`).show();
+ $(`#rpg-new-quest-${field}`).focus();
+ });
+
+ // Cancel add quest
+ $questsContainer.find('[data-action="cancel-add-quest"]').on('click', function() {
+ const field = $(this).data('field');
+ $(`#rpg-add-quest-form-${field}`).hide();
+ $(`#rpg-new-quest-${field}`).val('');
+ });
+
+ // Save add quest
+ $questsContainer.find('[data-action="save-add-quest"]').on('click', function() {
+ const field = $(this).data('field');
+ const input = $(`#rpg-new-quest-${field}`);
+ const questTitle = input.val().trim();
+
+ if (questTitle) {
+ if (field === 'main') {
+ extensionSettings.quests.main = questTitle;
+ } else {
+ if (!extensionSettings.quests.optional) {
+ extensionSettings.quests.optional = [];
+ }
+ extensionSettings.quests.optional.push(questTitle);
+ }
+ saveSettings();
+ renderQuests();
+ }
+ });
+
+ // Edit quest (main only)
+ $questsContainer.find('[data-action="edit-quest"]').on('click', function() {
+ const field = $(this).data('field');
+ $(`#rpg-edit-quest-form-${field}`).show();
+ $('.rpg-quest-item[data-field="main"]').hide();
+ $(`#rpg-edit-quest-${field}`).focus();
+ });
+
+ // Cancel edit quest
+ $questsContainer.find('[data-action="cancel-edit-quest"]').on('click', function() {
+ const field = $(this).data('field');
+ $(`#rpg-edit-quest-form-${field}`).hide();
+ $('.rpg-quest-item[data-field="main"]').show();
+ });
+
+ // Save edit quest
+ $questsContainer.find('[data-action="save-edit-quest"]').on('click', function() {
+ const field = $(this).data('field');
+ const input = $(`#rpg-edit-quest-${field}`);
+ const questTitle = input.val().trim();
+
+ if (questTitle) {
+ extensionSettings.quests.main = questTitle;
+ saveSettings();
+ renderQuests();
+ }
+ });
+
+ // Remove quest
+ $questsContainer.find('[data-action="remove-quest"]').on('click', function() {
+ const field = $(this).data('field');
+ const index = $(this).data('index');
+
+ if (field === 'main') {
+ extensionSettings.quests.main = 'None';
+ } else {
+ extensionSettings.quests.optional.splice(index, 1);
+ }
+ saveSettings();
+ renderQuests();
+ });
+
+ // Inline editing for optional quests
+ $questsContainer.find('.rpg-quest-title.rpg-editable').on('blur', function() {
+ const $this = $(this);
+ const field = $this.data('field');
+ const index = $this.data('index');
+ const newTitle = $this.text().trim();
+
+ if (newTitle && field === 'optional' && index !== undefined) {
+ extensionSettings.quests.optional[index] = newTitle;
+ saveSettings();
+ }
+ });
+
+ // Enter key to save in forms
+ $questsContainer.find('.rpg-inline-input').on('keypress', function(e) {
+ if (e.which === 13) {
+ const field = $(this).attr('id').includes('edit') ?
+ $(this).attr('id').replace('rpg-edit-quest-', '') :
+ $(this).attr('id').replace('rpg-new-quest-', '');
+
+ if ($(this).attr('id').includes('edit')) {
+ $(`[data-action="save-edit-quest"][data-field="${field}"]`).click();
+ } else {
+ $(`[data-action="save-add-quest"][data-field="${field}"]`).click();
+ }
+ }
+ });
+}
diff --git a/src/systems/rendering/thoughts.js b/src/systems/rendering/thoughts.js
index 02f073c..a4c0d89 100644
--- a/src/systems/rendering/thoughts.js
+++ b/src/systems/rendering/thoughts.js
@@ -76,15 +76,13 @@ export function renderThoughts() {
$thoughtsContainer.addClass('rpg-content-updating');
}
- // Initialize if no data yet
- if (!lastGeneratedData.characterThoughts) {
- lastGeneratedData.characterThoughts = '';
- }
+ // Use committedTrackerData as fallback if lastGeneratedData is empty (e.g., after page refresh)
+ const characterThoughtsData = lastGeneratedData.characterThoughts || committedTrackerData.characterThoughts || '';
- debugLog('[RPG Thoughts] Raw characterThoughts data:', lastGeneratedData.characterThoughts);
- debugLog('[RPG Thoughts] Data length:', lastGeneratedData.characterThoughts.length + ' chars');
+ debugLog('[RPG Thoughts] Raw characterThoughts data:', characterThoughtsData);
+ debugLog('[RPG Thoughts] Data length:', characterThoughtsData.length + ' chars');
- const lines = lastGeneratedData.characterThoughts.split('\n');
+ const lines = characterThoughtsData.split('\n');
const presentCharacters = [];
debugLog('[RPG Thoughts] Split into lines count:', lines.length);
@@ -378,8 +376,14 @@ export function updateCharacterField(characterName, field, value) {
if (emojiMatch) {
let emoji = emojiMatch[1].trim();
let info = emojiMatch[2].trim();
- let relationship = parts[1];
- let thoughts = parts[2] || '';
+ let relationship = parts[1] ? parts[1].trim() : '';
+ let thoughts = parts[2] ? parts[2].trim() : '';
+
+ // Handle 4-part format (with demeanor)
+ if (parts.length >= 4) {
+ relationship = parts[2] ? parts[2].trim() : '';
+ thoughts = parts[3] ? parts[3].trim() : '';
+ }
const infoParts = info.split(',').map(p => p.trim());
let name = infoParts[0];
diff --git a/src/systems/rendering/userStats.js b/src/systems/rendering/userStats.js
index eed3789..b51299f 100644
--- a/src/systems/rendering/userStats.js
+++ b/src/systems/rendering/userStats.js
@@ -20,9 +20,28 @@ import {
import { getSafeThumbnailUrl } from '../../utils/avatars.js';
import { buildInventorySummary } from '../generation/promptBuilder.js';
+/**
+ * Builds the user stats text string using custom stat names
+ * @returns {string} Formatted stats text for tracker
+ */
+export function buildUserStatsText() {
+ const stats = extensionSettings.userStats;
+ const statNames = extensionSettings.statNames || {
+ health: 'Health',
+ satiety: 'Satiety',
+ energy: 'Energy',
+ hygiene: 'Hygiene',
+ arousal: 'Arousal'
+ };
+ const inventorySummary = buildInventorySummary(stats.inventory);
+
+ return `${statNames.health}: ${stats.health}%\n${statNames.satiety}: ${stats.satiety}%\n${statNames.energy}: ${stats.energy}%\n${statNames.hygiene}: ${stats.hygiene}%\n${statNames.arousal}: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`;
+}
+
/**
* Renders the user stats panel with health bars, mood, inventory, and classic stats.
* Includes event listeners for editable fields.
+```
*/
export function renderUserStats() {
if (!extensionSettings.showUserStats || !$userStatsContainer) {
@@ -30,11 +49,18 @@ export function renderUserStats() {
}
const stats = extensionSettings.userStats;
+ const statNames = extensionSettings.statNames || {
+ health: 'Health',
+ satiety: 'Satiety',
+ energy: 'Energy',
+ hygiene: 'Hygiene',
+ arousal: 'Arousal'
+ };
const userName = getContext().name1;
// Initialize lastGeneratedData.userStats if it doesn't exist
if (!lastGeneratedData.userStats) {
- 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}`;
+ lastGeneratedData.userStats = buildUserStatsText();
}
// Get user portrait - handle both default-user and custom persona folders
@@ -64,7 +90,7 @@ export function renderUserStats() {
-
Health:
+
${statNames.health}:
@@ -72,7 +98,7 @@ export function renderUserStats() {
-
Satiety:
+
${statNames.satiety}:
@@ -80,7 +106,7 @@ export function renderUserStats() {
-
Energy:
+
${statNames.energy}:
@@ -88,7 +114,7 @@ export function renderUserStats() {
-
Hygiene:
+
${statNames.hygiene}:
@@ -96,7 +122,7 @@ export function renderUserStats() {
-
Arousal:
+
${statNames.arousal}:
@@ -184,10 +210,8 @@ export function renderUserStats() {
// Update the setting
extensionSettings.userStats[field] = value;
- // Rebuild userStats text with proper inventory format
- const stats = extensionSettings.userStats;
- const inventorySummary = buildInventorySummary(stats.inventory);
- const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`;
+ // Rebuild userStats text with custom stat names
+ const statsText = buildUserStatsText();
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
@@ -207,10 +231,8 @@ export function renderUserStats() {
const value = $(this).text().trim();
extensionSettings.userStats.mood = value || '😐';
- // Rebuild userStats text with proper inventory format
- const stats = extensionSettings.userStats;
- const inventorySummary = buildInventorySummary(stats.inventory);
- const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`;
+ // Rebuild userStats text with custom stat names
+ const statsText = buildUserStatsText();
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
@@ -226,10 +248,8 @@ export function renderUserStats() {
const value = $(this).text().trim();
extensionSettings.userStats.conditions = value || 'None';
- // Rebuild userStats text with proper inventory format
- const stats = extensionSettings.userStats;
- const inventorySummary = buildInventorySummary(stats.inventory);
- const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`;
+ // Rebuild userStats text with custom stat names
+ const statsText = buildUserStatsText();
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
@@ -241,6 +261,30 @@ export function renderUserStats() {
updateMessageSwipeData();
});
+ // Add event listeners for stat name editing
+ $('.rpg-editable-stat-name').on('blur', function() {
+ const field = $(this).data('field');
+ const value = $(this).text().trim().replace(':', '');
+
+ if (!extensionSettings.statNames) {
+ extensionSettings.statNames = {
+ health: 'Health',
+ satiety: 'Satiety',
+ energy: 'Energy',
+ hygiene: 'Hygiene',
+ arousal: 'Arousal'
+ };
+ }
+
+ extensionSettings.statNames[field] = value || extensionSettings.statNames[field];
+
+ saveSettings();
+ saveChatData();
+
+ // Re-render to update the display
+ renderUserStats();
+ });
+
// Add event listener for level editing
$('.rpg-level-value.rpg-editable').on('blur', function() {
let value = parseInt($(this).text().trim());
diff --git a/src/systems/ui/desktop.js b/src/systems/ui/desktop.js
index 2b8e2a4..b7e07e2 100644
--- a/src/systems/ui/desktop.js
+++ b/src/systems/ui/desktop.js
@@ -22,9 +22,10 @@ export function setupDesktopTabs() {
const $infoBox = $('#rpg-info-box');
const $thoughts = $('#rpg-thoughts');
const $inventory = $('#rpg-inventory');
+ const $quests = $('#rpg-quests');
// If no sections exist, nothing to organize
- if ($userStats.length === 0 && $infoBox.length === 0 && $thoughts.length === 0 && $inventory.length === 0) {
+ if ($userStats.length === 0 && $infoBox.length === 0 && $thoughts.length === 0 && $inventory.length === 0 && $quests.length === 0) {
return;
}
@@ -39,12 +40,17 @@ export function setupDesktopTabs() {
Inventory
+
`);
// Create tab content containers
const $statusTab = $('
');
const $inventoryTab = $('
');
+ const $questsTab = $('
');
// Move sections into their respective tabs (detach to preserve event handlers)
if ($userStats.length > 0) {
@@ -63,6 +69,10 @@ export function setupDesktopTabs() {
$inventoryTab.append($inventory.detach());
$inventory.show();
}
+ if ($quests.length > 0) {
+ $questsTab.append($quests.detach());
+ $quests.show();
+ }
// Hide dividers on desktop tabs (tabs separate content naturally)
$('.rpg-divider').hide();
@@ -72,6 +82,7 @@ export function setupDesktopTabs() {
$tabsContainer.append($tabNav);
$tabsContainer.append($statusTab);
$tabsContainer.append($inventoryTab);
+ $tabsContainer.append($questsTab);
// Replace content box with tabs container
$contentBox.html('').append($tabsContainer);
@@ -102,6 +113,7 @@ export function removeDesktopTabs() {
const $infoBox = $('#rpg-info-box').detach();
const $thoughts = $('#rpg-thoughts').detach();
const $inventory = $('#rpg-inventory').detach();
+ const $quests = $('#rpg-quests').detach();
// Remove tabs container
$('.rpg-tabs-container').remove();
@@ -114,18 +126,20 @@ export function removeDesktopTabs() {
// Restore original sections to content box in correct order
const $contentBox = $('.rpg-content-box');
- // Re-insert sections in original order: User Stats, Info Box, Thoughts, Inventory
+ // Re-insert sections in original order: User Stats, Info Box, Thoughts, Inventory, Quests
if ($dividerStats.length) {
$dividerStats.before($userStats);
$dividerInfo.before($infoBox);
$dividerThoughts.before($thoughts);
$contentBox.append($inventory);
+ $contentBox.append($quests);
} else {
// Fallback if dividers don't exist
$contentBox.append($userStats);
$contentBox.append($infoBox);
$contentBox.append($thoughts);
$contentBox.append($inventory);
+ $contentBox.append($quests);
}
// Show sections and dividers
diff --git a/src/systems/ui/layout.js b/src/systems/ui/layout.js
index 597183c..9993154 100644
--- a/src/systems/ui/layout.js
+++ b/src/systems/ui/layout.js
@@ -266,15 +266,11 @@ export function applyPanelPosition() {
* Updates the UI based on generation mode selection.
*/
export function updateGenerationModeUI() {
- const $mobileBtn = $('#rpg-manual-update-mobile');
-
if (extensionSettings.generationMode === 'together') {
- // In "together" mode, hide both desktop and mobile refresh buttons
+ // In "together" mode, manual update button is hidden
$('#rpg-manual-update').hide();
- $mobileBtn.addClass('rpg-hidden-mode');
} else {
- // In "separate" mode, show both desktop and mobile refresh buttons
+ // In "separate" mode, manual update button is visible
$('#rpg-manual-update').show();
- $mobileBtn.removeClass('rpg-hidden-mode');
}
}
diff --git a/src/systems/ui/mobile.js b/src/systems/ui/mobile.js
index 817e62a..263e5e5 100644
--- a/src/systems/ui/mobile.js
+++ b/src/systems/ui/mobile.js
@@ -527,9 +527,10 @@ export function setupMobileTabs() {
const $infoBox = $('#rpg-info-box');
const $thoughts = $('#rpg-thoughts');
const $inventory = $('#rpg-inventory');
+ const $quests = $('#rpg-quests');
// If no sections exist, nothing to organize
- if ($userStats.length === 0 && $infoBox.length === 0 && $thoughts.length === 0 && $inventory.length === 0) {
+ if ($userStats.length === 0 && $infoBox.length === 0 && $thoughts.length === 0 && $inventory.length === 0 && $quests.length === 0) {
return;
}
@@ -538,6 +539,7 @@ export function setupMobileTabs() {
const hasStats = $userStats.length > 0;
const hasInfo = $infoBox.length > 0 || $thoughts.length > 0;
const hasInventory = $inventory.length > 0;
+ const hasQuests = $quests.length > 0;
// Tab 1: Stats (User Stats only)
if (hasStats) {
@@ -551,6 +553,10 @@ export function setupMobileTabs() {
if (hasInventory) {
tabs.push('
');
}
+ // Tab 4: Quests
+ if (hasQuests) {
+ tabs.push('
');
+ }
const $tabNav = $('
' + tabs.join('') + '
');
@@ -559,11 +565,13 @@ export function setupMobileTabs() {
if (hasStats) firstTab = 'stats';
else if (hasInfo) firstTab = 'info';
else if (hasInventory) firstTab = 'inventory';
+ else if (hasQuests) firstTab = 'quests';
// Create tab content wrappers
const $statsTab = $('
');
const $infoTab = $('
');
const $inventoryTab = $('
');
+ const $questsTab = $('
');
// Move sections into their respective tabs (detach to preserve event handlers)
// Stats tab: User Stats only
@@ -588,6 +596,12 @@ export function setupMobileTabs() {
$inventory.show();
}
+ // Quests tab: Quests only
+ if ($quests.length > 0) {
+ $questsTab.append($quests.detach());
+ $quests.show();
+ }
+
// Hide dividers on mobile
$('.rpg-divider').hide();
@@ -599,6 +613,8 @@ export function setupMobileTabs() {
if (hasStats) $mobileContainer.append($statsTab);
if (hasInfo) $mobileContainer.append($infoTab);
if (hasInventory) $mobileContainer.append($inventoryTab);
+ if (hasQuests) $mobileContainer.append($questsTab);
+ if (hasInventory) $mobileContainer.append($inventoryTab);
// Insert mobile tab structure at the beginning of content box
$contentBox.prepend($mobileContainer);
@@ -626,6 +642,7 @@ export function removeMobileTabs() {
const $infoBox = $('#rpg-info-box').detach();
const $thoughts = $('#rpg-thoughts').detach();
const $inventory = $('#rpg-inventory').detach();
+ const $quests = $('#rpg-quests').detach();
// Remove mobile tab container
$('.rpg-mobile-container').remove();
@@ -638,14 +655,16 @@ export function removeMobileTabs() {
// Restore original sections to content box in correct order
const $contentBox = $('.rpg-content-box');
- // Re-insert sections in original order: User Stats, Info Box, Thoughts, Inventory
+ // Re-insert sections in original order: User Stats, Info Box, Thoughts, Inventory, Quests
if ($dividerStats.length) {
$dividerStats.before($userStats);
$dividerInfo.before($infoBox);
$dividerThoughts.before($thoughts);
$contentBox.append($inventory);
+ $contentBox.append($quests);
} else {
// Fallback if dividers don't exist
+ $contentBox.prepend($quests);
$contentBox.prepend($inventory);
$contentBox.prepend($thoughts);
$contentBox.prepend($infoBox);
diff --git a/src/systems/ui/modals.js b/src/systems/ui/modals.js
index 1e43847..0191ef7 100644
--- a/src/systems/ui/modals.js
+++ b/src/systems/ui/modals.js
@@ -16,6 +16,7 @@ import {
import { saveSettings, saveChatData } from '../../core/persistence.js';
import { renderUserStats } from '../rendering/userStats.js';
import { updateChatThoughts } from '../rendering/thoughts.js';
+import { renderQuests } from '../rendering/quests.js';
import {
rollDice as rollDiceCore,
clearDiceRoll as clearDiceRollCore,
@@ -409,6 +410,12 @@ export function setupSettingsPopup() {
// Clear dice roll
extensionSettings.lastDiceRoll = null;
+ // Clear quests
+ extensionSettings.quests = {
+ main: "None",
+ optional: []
+ };
+
// Save everything
saveChatData();
saveSettings();
@@ -417,6 +424,7 @@ export function setupSettingsPopup() {
renderUserStats();
updateDiceDisplayCore();
updateChatThoughts(); // Clear the thought bubble in chat
+ renderQuests(); // Clear and re-render quests UI
// console.log('[RPG Companion] Chat cache cleared');
});
diff --git a/style.css b/style.css
index edf9d0d..ab0365b 100644
--- a/style.css
+++ b/style.css
@@ -576,8 +576,8 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-user-portrait {
- width: clamp(1.7vw, 1.8vw, 1.9vw);
- height: clamp(1.7vw, 1.8vw, 1.9vw);
+ width: clamp(24px, 4vh, 32px);
+ height: clamp(24px, 4vh, 32px);
border-radius: 50%;
border: 2px solid var(--rpg-highlight);
box-shadow: 0 0 8px var(--rpg-highlight);
@@ -726,7 +726,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* User name and level - inline with portrait */
.rpg-user-name {
font-weight: 600;
- font-size: 0.7vw;
+ font-size: 1em;
color: var(--rpg-text-color);
white-space: nowrap;
overflow: hidden;
@@ -734,14 +734,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-level-label {
- font-size: 0.7vw;
+ font-size: 1em;
font-weight: 600;
color: var(--rpg-text-color);
opacity: 0.7;
}
.rpg-level-value {
- font-size: 0.7vw;
+ font-size: 1em;
font-weight: 700;
color: var(--rpg-highlight-color);
padding: clamp(1px, 0.2vh, 2px) 0.375em;
@@ -2088,17 +2088,45 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border-radius: 0.75em;
padding: 0.375em;
margin-bottom: 0;
- flex: 0 0 auto;
+ flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
gap: 0.25em;
align-items: stretch;
width: 100%;
+ overflow: hidden;
+}
+
+/* Scrollable content wrapper inside info section */
+.rpg-info-content {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25em;
+ flex: 1;
+ min-height: 0;
overflow-y: auto;
overflow-x: hidden;
}
+.rpg-info-content::-webkit-scrollbar {
+ width: 0.188rem;
+}
+
+.rpg-info-content::-webkit-scrollbar-track {
+ background: var(--rpg-bg);
+ border-radius: 2px;
+}
+
+.rpg-info-content::-webkit-scrollbar-thumb {
+ background: var(--rpg-highlight);
+ border-radius: 2px;
+}
+
+.rpg-info-content::-webkit-scrollbar-thumb:hover {
+ background: var(--rpg-text);
+}
+
.rpg-dashboard {
display: flex;
gap: 0.25em;
@@ -2422,6 +2450,161 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
flex-shrink: 0;
}
+/* Row 3: Recent Events */
+.rpg-dashboard-row-3 {
+ flex: 0 0 auto;
+ min-height: 0;
+}
+
+.rpg-dashboard-row-3 .rpg-dashboard-widget {
+ flex: 1;
+ width: 100%;
+}
+
+/* Recent Events Widget - Notebook Style */
+.rpg-events-widget {
+ background: linear-gradient(to bottom,
+ rgba(255, 248, 220, 0.08) 0%,
+ rgba(255, 248, 220, 0.12) 50%,
+ rgba(255, 248, 220, 0.08) 100%);
+ border: 2px solid var(--rpg-border);
+ border-radius: 0.375em;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+ min-height: 0;
+ overflow: visible;
+ position: relative;
+}
+
+/* Notebook paper lines effect */
+.rpg-events-widget::before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-image: repeating-linear-gradient(
+ transparent,
+ transparent calc(1em + 0.5em),
+ rgba(var(--SmartThemeBorderColor), 0.08) calc(1em + 0.5em),
+ rgba(var(--SmartThemeBorderColor), 0.08) calc(1em + 0.6em)
+ );
+ pointer-events: none;
+ z-index: 0;
+}
+
+.rpg-events-widget:hover {
+ transform: translateY(-0.125rem);
+ box-shadow: 0 4px 12px var(--rpg-shadow);
+}
+
+/* Notebook ring binding at top */
+.rpg-notebook-header {
+ display: flex;
+ justify-content: center;
+ gap: 1.5em;
+ padding: 0.25em 0;
+ background: rgba(0, 0, 0, 0.2);
+ border-bottom: 1px solid var(--rpg-border);
+ position: relative;
+ z-index: 1;
+}
+
+.rpg-notebook-ring {
+ width: 0.5em;
+ height: 0.5em;
+ border-radius: 50%;
+ background: radial-gradient(circle at 30% 30%,
+ rgba(80, 80, 80, 0.4),
+ rgba(40, 40, 40, 0.6));
+ border: 1px solid rgba(0, 0, 0, 0.5);
+ box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.2),
+ 0 1px 2px rgba(0, 0, 0, 0.3);
+}
+
+.rpg-notebook-title {
+ font-size: clamp(0.5vw, 0.6vw, 0.7vw);
+ font-weight: bold;
+ color: var(--rpg-highlight);
+ text-align: center;
+ padding: 0.25em 0.5em 0.25em 0.5em;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ opacity: 0.9;
+ position: relative;
+ z-index: 1;
+}
+
+.rpg-notebook-lines {
+ display: flex;
+ flex-direction: column;
+ gap: 0.125em;
+ padding: 0.25em 0.75em 0.5em 0.75em;
+ position: relative;
+ z-index: 1;
+}
+
+.rpg-notebook-line {
+ display: flex;
+ align-items: flex-start;
+ gap: 0.375em;
+ position: relative;
+}
+
+.rpg-notebook-line.rpg-event-add {
+ opacity: 0.5;
+}
+
+.rpg-notebook-line.rpg-event-add:hover {
+ opacity: 0.8;
+}
+
+.rpg-bullet {
+ font-size: clamp(0.5vw, 0.6vw, 0.7vw);
+ color: var(--rpg-highlight);
+ flex-shrink: 0;
+ line-height: 1.4;
+ opacity: 0.8;
+}
+
+.rpg-event-add .rpg-bullet {
+ color: var(--rpg-text);
+ opacity: 0.6;
+}
+
+.rpg-event-text {
+ font-size: clamp(0.45vw, 0.55vw, 0.65vw);
+ color: var(--rpg-text);
+ line-height: 1.4;
+ flex: 1;
+ word-wrap: break-word;
+ cursor: text;
+ padding: 0.125em 0.25em;
+ border-radius: 0.2em;
+ transition: background 0.2s ease;
+ opacity: 0.85;
+}
+
+.rpg-event-text.rpg-event-placeholder {
+ opacity: 0.5;
+ font-style: italic;
+}
+
+.rpg-event-text:hover {
+ background: rgba(255, 255, 255, 0.05);
+ opacity: 1;
+}
+
+.rpg-event-text:focus {
+ background: rgba(255, 255, 255, 0.1);
+ outline: 1px solid var(--rpg-highlight);
+ opacity: 1;
+}
+
/* Character Status Cards */
.rpg-character-status {
display: flex;
@@ -2776,7 +2959,8 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* Editable field styles */
.rpg-editable,
-.rpg-editable-stat {
+.rpg-editable-stat,
+.rpg-editable-stat-name {
cursor: text;
transition: all 0.2s ease;
border-radius: 2px;
@@ -2784,13 +2968,15 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
.rpg-editable:hover,
-.rpg-editable-stat:hover {
+.rpg-editable-stat:hover,
+.rpg-editable-stat-name:hover {
background: var(--rpg-accent);
outline: 1px solid var(--rpg-highlight);
}
.rpg-editable:focus,
-.rpg-editable-stat:focus {
+.rpg-editable-stat:focus,
+.rpg-editable-stat-name:focus {
background: var(--rpg-bg);
outline: 2px solid var(--rpg-highlight);
box-shadow: 0 0 8px var(--rpg-highlight);
@@ -3117,6 +3303,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
transform: translateY(0);
}
+/* Reset FAB Positions Button - Similar to clear cache but different color */
.rpg-btn-reset-fab {
width: 100%;
padding: 0.625em;
@@ -3145,6 +3332,276 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
transform: translateY(0);
}
+/* ============================================
+ MEMORY RECOLLECTION STYLES
+ ============================================ */
+
+/* Memory Recollection Button */
+.rpg-memory-recollection-btn {
+ width: 100%;
+ padding: 0.75em 1em;
+ margin-bottom: 10px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border: 2px solid rgba(102, 126, 234, 0.5);
+ border-radius: 0.5em;
+ color: #ffffff;
+ font-size: 14px;
+ font-weight: 600;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5em;
+ transition: all 0.3s ease;
+}
+
+.rpg-memory-recollection-btn:hover {
+ background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
+ border-color: rgba(102, 126, 234, 0.8);
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+}
+
+.rpg-memory-recollection-btn:active {
+ transform: translateY(0);
+}
+
+/* Modal Overlay */
+.rpg-memory-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.7);
+ backdrop-filter: blur(5px);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 10000;
+ animation: fadeIn 0.2s ease;
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+/* Modal Container */
+.rpg-memory-modal {
+ background: var(--SmartThemeBlurTintColor, #1a1a2e);
+ border: 2px solid var(--SmartThemeBorderColor, #667eea);
+ border-radius: 12px;
+ max-width: 500px;
+ width: 90%;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
+ animation: modalSlideIn 0.3s ease;
+}
+
+@keyframes modalSlideIn {
+ from {
+ transform: translateY(-20px);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+
+/* Modal Header */
+.rpg-memory-modal-header {
+ padding: 1.25em;
+ border-bottom: 1px solid var(--SmartThemeBorderColor, #667eea);
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
+}
+
+.rpg-memory-modal-header h3 {
+ margin: 0;
+ color: var(--SmartThemeBodyColor, #eaeaea);
+ font-size: 1.25em;
+ font-weight: 600;
+}
+
+/* Modal Body */
+.rpg-memory-modal-body {
+ padding: 1.5em;
+ color: var(--SmartThemeBodyColor, #eaeaea);
+}
+
+.rpg-memory-modal-body p {
+ margin: 0.75em 0;
+ line-height: 1.6;
+}
+
+.rpg-memory-modal-info {
+ background: rgba(102, 126, 234, 0.1);
+ padding: 1em;
+ border-radius: 8px;
+ border-left: 4px solid #667eea;
+ margin-top: 1em;
+}
+
+.rpg-memory-modal-hint {
+ font-size: 0.85em;
+ color: #999;
+}
+
+/* Progress Elements */
+.rpg-memory-progress-text {
+ text-align: center;
+ font-weight: 600;
+ margin-bottom: 1em;
+}
+
+.rpg-memory-progress-bar {
+ width: 100%;
+ height: 30px;
+ background: rgba(0, 0, 0, 0.3);
+ border-radius: 15px;
+ overflow: hidden;
+ position: relative;
+}
+
+.rpg-memory-progress-fill {
+ height: 100%;
+ width: 0%;
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
+ transition: width 0.3s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-weight: 600;
+ font-size: 0.85em;
+}
+
+.rpg-memory-status {
+ margin-top: 1em;
+ padding: 0.75em;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ font-size: 0.9em;
+ color: #999;
+ max-height: 100px;
+ overflow-y: auto;
+}
+
+/* Modal Footer */
+.rpg-memory-modal-footer {
+ padding: 1em 1.25em;
+ border-top: 1px solid var(--SmartThemeBorderColor, #667eea);
+ display: flex;
+ gap: 0.75em;
+ justify-content: flex-end;
+}
+
+/* Modal Buttons */
+.rpg-memory-modal-btn {
+ padding: 0.625em 1.25em;
+ border-radius: 6px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ border: none;
+ font-size: 14px;
+}
+
+.rpg-memory-cancel {
+ background: rgba(220, 53, 69, 0.2);
+ color: #ff6b6b;
+ border: 2px solid rgba(220, 53, 69, 0.5);
+}
+
+.rpg-memory-cancel:hover {
+ background: rgba(220, 53, 69, 0.3);
+ border-color: rgba(220, 53, 69, 0.8);
+}
+
+.rpg-memory-proceed {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ border: 2px solid rgba(102, 126, 234, 0.5);
+}
+
+.rpg-memory-proceed:hover {
+ background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
+ border-color: rgba(102, 126, 234, 0.8);
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
+}
+
+/* ============================================
+ LOREBOOK LIMITER STYLING
+ ============================================ */
+
+.rpg-lorebook-limiter-container {
+ width: 100%;
+ padding: 0.75em 1em;
+ margin-bottom: 10px;
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.15) 0%, rgba(118, 75, 162, 0.15) 100%);
+ border: 2px solid rgba(102, 126, 234, 0.3);
+ border-radius: 0.5em;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5em;
+}
+
+.rpg-lorebook-limiter-label {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1em;
+ cursor: pointer;
+}
+
+.rpg-lorebook-limiter-title {
+ color: rgba(102, 126, 234, 1);
+ font-weight: 600;
+ font-size: 14px;
+}
+
+.rpg-lorebook-limiter-input {
+ width: 120px;
+ padding: 0.5em;
+ background: rgba(0, 0, 0, 0.3);
+ border: 2px solid rgba(102, 126, 234, 0.4);
+ border-radius: 0.3em;
+ color: #ffffff;
+ font-size: 14px;
+ font-weight: 600;
+ text-align: center;
+ transition: all 0.3s ease;
+}
+
+.rpg-lorebook-limiter-input:focus {
+ outline: none;
+ border-color: rgba(102, 126, 234, 0.8);
+ background: rgba(0, 0, 0, 0.5);
+ box-shadow: 0 0 8px rgba(102, 126, 234, 0.3);
+}
+
+.rpg-lorebook-limiter-input::placeholder {
+ color: rgba(255, 255, 255, 0.4);
+ font-weight: 400;
+}
+
+.rpg-lorebook-limiter-hint {
+ color: rgba(255, 255, 255, 0.6);
+ font-size: 12px;
+ font-style: italic;
+}
+
+.rpg-memory-close {
+ background: rgba(52, 152, 219, 0.2);
+ color: #5dade2;
+ border: 2px solid rgba(52, 152, 219, 0.5);
+}
+
+.rpg-memory-close:hover {
+ background: rgba(52, 152, 219, 0.3);
+ border-color: rgba(52, 152, 219, 0.8);
+}
+
/* ============================================
THEME VARIATIONS
============================================ */
@@ -3309,6 +3766,215 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
);
}
+/* ============================================
+ THEME SUPPORT FOR ALL PANEL ELEMENTS
+ ============================================ */
+
+/* Apply theme colors to tabs navigation */
+.rpg-panel[data-theme="sci-fi"] .rpg-tabs-nav,
+.rpg-panel[data-theme="fantasy"] .rpg-tabs-nav,
+.rpg-panel[data-theme="cyberpunk"] .rpg-tabs-nav {
+ border-bottom-color: var(--rpg-border);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-tab-btn,
+.rpg-panel[data-theme="fantasy"] .rpg-tab-btn,
+.rpg-panel[data-theme="cyberpunk"] .rpg-tab-btn {
+ background: var(--rpg-accent);
+ border-color: var(--rpg-border);
+ color: var(--rpg-text);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-tab-btn:hover,
+.rpg-panel[data-theme="fantasy"] .rpg-tab-btn:hover,
+.rpg-panel[data-theme="cyberpunk"] .rpg-tab-btn:hover {
+ background: var(--rpg-highlight);
+ border-color: var(--rpg-highlight);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-tab-btn.active,
+.rpg-panel[data-theme="fantasy"] .rpg-tab-btn.active,
+.rpg-panel[data-theme="cyberpunk"] .rpg-tab-btn.active {
+ background: var(--rpg-highlight);
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-bg);
+}
+
+/* Apply theme colors to inventory subtabs */
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-subtabs,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-subtabs,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-subtabs {
+ border-bottom-color: var(--rpg-border);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-subtab,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-subtab,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-subtab {
+ border-color: var(--rpg-border);
+ color: var(--rpg-text);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-subtab:hover,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-subtab:hover,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-subtab:hover {
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-subtab.active,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-subtab.active,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-subtab.active {
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
+}
+
+/* Apply theme colors to quests subtabs */
+.rpg-panel[data-theme="sci-fi"] .rpg-quests-subtabs,
+.rpg-panel[data-theme="fantasy"] .rpg-quests-subtabs,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quests-subtabs {
+ border-bottom-color: var(--rpg-border);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-quests-subtab,
+.rpg-panel[data-theme="fantasy"] .rpg-quests-subtab,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quests-subtab {
+ border-color: var(--rpg-border);
+ color: var(--rpg-text);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-quests-subtab:hover,
+.rpg-panel[data-theme="fantasy"] .rpg-quests-subtab:hover,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quests-subtab:hover {
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-quests-subtab.active,
+.rpg-panel[data-theme="fantasy"] .rpg-quests-subtab.active,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quests-subtab.active {
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
+}
+
+/* Apply theme colors to storage locations */
+.rpg-panel[data-theme="sci-fi"] .rpg-storage-location,
+.rpg-panel[data-theme="fantasy"] .rpg-storage-location,
+.rpg-panel[data-theme="cyberpunk"] .rpg-storage-location {
+ border-color: var(--rpg-border);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-storage-header,
+.rpg-panel[data-theme="fantasy"] .rpg-storage-header,
+.rpg-panel[data-theme="cyberpunk"] .rpg-storage-header {
+ background: var(--rpg-highlight);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-storage-content,
+.rpg-panel[data-theme="fantasy"] .rpg-storage-content,
+.rpg-panel[data-theme="cyberpunk"] .rpg-storage-content {
+ background: var(--rpg-accent);
+}
+
+/* Apply theme colors to buttons */
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-edit-btn,
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-add-btn,
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-remove-btn,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-edit,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-remove,
+.rpg-panel[data-theme="sci-fi"] .rpg-add-quest-btn,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-edit-btn,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-add-btn,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-remove-btn,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-edit,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-remove,
+.rpg-panel[data-theme="fantasy"] .rpg-add-quest-btn,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-edit-btn,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-add-btn,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-remove-btn,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-edit,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-remove,
+.rpg-panel[data-theme="cyberpunk"] .rpg-add-quest-btn {
+ background: var(--rpg-accent);
+ border-color: var(--rpg-border);
+ color: var(--rpg-text);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-add-btn,
+.rpg-panel[data-theme="sci-fi"] .rpg-add-quest-btn,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-add-btn,
+.rpg-panel[data-theme="fantasy"] .rpg-add-quest-btn,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-add-btn,
+.rpg-panel[data-theme="cyberpunk"] .rpg-add-quest-btn {
+ background: transparent;
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
+}
+
+/* Apply theme colors to inventory/quest items */
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-text,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-item,
+.rpg-panel[data-theme="sci-fi"] .rpg-main-quest-display,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-text,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-item,
+.rpg-panel[data-theme="fantasy"] .rpg-main-quest-display,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-text,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-item,
+.rpg-panel[data-theme="cyberpunk"] .rpg-main-quest-display {
+ background: var(--rpg-accent);
+ border-color: var(--rpg-border);
+ color: var(--rpg-text);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-item:hover,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-item:hover,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-item:hover {
+ border-color: var(--rpg-highlight);
+}
+
+/* Apply theme colors to hints and empty states */
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-hint,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-hint,
+.rpg-panel[data-theme="sci-fi"] .rpg-inventory-empty,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-empty,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-hint,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-hint,
+.rpg-panel[data-theme="fantasy"] .rpg-inventory-empty,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-empty,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-hint,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-hint,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inventory-empty,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-empty {
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-text);
+}
+
+/* Apply theme colors to input fields */
+.rpg-panel[data-theme="sci-fi"] .rpg-inline-input,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-edit-form input,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-edit-form textarea,
+.rpg-panel[data-theme="fantasy"] .rpg-inline-input,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-edit-form input,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-edit-form textarea,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inline-input,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-edit-form input,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-edit-form textarea {
+ background: var(--rpg-bg);
+ border-color: var(--rpg-border);
+ color: var(--rpg-text);
+}
+
+.rpg-panel[data-theme="sci-fi"] .rpg-inline-input:focus,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-edit-form input:focus,
+.rpg-panel[data-theme="sci-fi"] .rpg-quest-edit-form textarea:focus,
+.rpg-panel[data-theme="fantasy"] .rpg-inline-input:focus,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-edit-form input:focus,
+.rpg-panel[data-theme="fantasy"] .rpg-quest-edit-form textarea:focus,
+.rpg-panel[data-theme="cyberpunk"] .rpg-inline-input:focus,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-edit-form input:focus,
+.rpg-panel[data-theme="cyberpunk"] .rpg-quest-edit-form textarea:focus {
+ border-color: var(--rpg-highlight);
+}
+
/* ============================================
RESPONSIVE DESIGN
============================================ */
@@ -4381,66 +5047,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
transform: rotate(180deg);
}
-/* ============================================
- MOBILE REFRESH FAB BUTTON (Same pattern as mobile toggle)
- ============================================ */
-.rpg-mobile-refresh {
- display: none;
- align-items: center;
- justify-content: center;
- position: fixed;
- /* Position set by JavaScript based on saved settings */
- width: 44px;
- height: 44px;
- border-radius: 50%;
- background: var(--SmartThemeBlurTintColor);
- border: 2px solid var(--SmartThemeBorderColor);
- color: var(--SmartThemeBodyColor);
- font-size: 1.125rem;
- cursor: grab;
- z-index: 1001; /* Below mobile toggle (10002), above debug (1000) */
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
- transition: opacity 0.3s ease, transform 0.2s ease, top 0.3s ease, left 0.3s ease, right 0.3s ease, bottom 0.3s ease;
- user-select: none;
- -webkit-user-select: none;
- will-change: top, left;
- /* Hidden by default - shown when panel open AND separate mode */
- opacity: 0;
- pointer-events: none;
-}
-
-.rpg-mobile-refresh.dragging {
- transition: none;
- cursor: grabbing;
-}
-
-.rpg-mobile-refresh:hover {
- transform: scale(1.1);
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
-}
-
-.rpg-mobile-refresh:active {
- transform: scale(0.95);
-}
-
-/* Spinning animation when refreshing */
-.rpg-mobile-refresh.spinning i {
- animation: rpg-spin 0.8s linear infinite;
-}
-
-.rpg-mobile-refresh i {
- pointer-events: none;
-}
-
-@keyframes rpg-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
-
/* Mobile overlay backdrop */
.rpg-mobile-overlay {
display: none;
@@ -5331,11 +5937,6 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
font-size: clamp(20px, 5.1vw, 26px) !important;
}
- /* Larger mobile refresh icon */
- .rpg-mobile-refresh {
- font-size: clamp(20px, 5.1vw, 26px) !important;
- }
-
/* ========================================
MOBILE SETTINGS POPUP
======================================== */
@@ -5367,10 +5968,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
/* Readable clear cache button */
- .rpg-btn-clear-cache {
- font-size: clamp(13px, 3.3vw, 17px) !important;
- }
-
+ .rpg-btn-clear-cache,
.rpg-btn-reset-fab {
font-size: clamp(13px, 3.3vw, 17px) !important;
}
@@ -5446,6 +6044,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
======================================== */
/* Inventory Container */
+#rpg-inventory {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ padding: 0.5rem;
+ font-size: 0.9rem;
+}
+
.rpg-inventory-container {
display: flex;
flex-direction: column;
@@ -5477,10 +6083,34 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
gap: 0.5rem;
border-bottom: 2px solid var(--SmartThemeBorderColor);
padding-bottom: 0.5rem;
+ overflow-x: auto;
+ overflow-y: hidden;
+ flex-wrap: nowrap;
+ scrollbar-width: thin;
+ scrollbar-color: var(--SmartThemeBorderColor) transparent;
+}
+
+.rpg-inventory-subtabs::-webkit-scrollbar {
+ height: 6px;
+}
+
+.rpg-inventory-subtabs::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.rpg-inventory-subtabs::-webkit-scrollbar-thumb {
+ background: var(--SmartThemeBorderColor);
+ border-radius: 3px;
+}
+
+.rpg-inventory-subtabs::-webkit-scrollbar-thumb:hover {
+ background: var(--rpg-accent);
}
.rpg-inventory-subtab {
flex: 1;
+ min-width: fit-content;
+ white-space: nowrap;
padding: 0.5rem 1rem;
background: transparent;
border: 2px solid var(--SmartThemeBorderColor);
@@ -5529,12 +6159,37 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
align-items: center;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--SmartThemeBorderColor);
+ gap: 0.5rem;
+ overflow-x: auto;
+ overflow-y: hidden;
+ flex-wrap: nowrap;
+ scrollbar-width: thin;
+ scrollbar-color: var(--SmartThemeBorderColor) transparent;
+}
+
+.rpg-inventory-header::-webkit-scrollbar {
+ height: 6px;
+}
+
+.rpg-inventory-header::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.rpg-inventory-header::-webkit-scrollbar-thumb {
+ background: var(--SmartThemeBorderColor);
+ border-radius: 3px;
+}
+
+.rpg-inventory-header::-webkit-scrollbar-thumb:hover {
+ background: var(--rpg-accent);
}
.rpg-inventory-header h4 {
margin: 0;
font-size: 1.1rem;
color: var(--SmartThemeBodyColor);
+ white-space: nowrap;
+ min-width: fit-content;
}
.rpg-inventory-content {
@@ -5664,6 +6319,8 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
background: transparent;
border-color: var(--rpg-highlight);
color: var(--rpg-highlight);
+ white-space: nowrap;
+ min-width: fit-content;
}
.rpg-inventory-add-btn:hover {
@@ -5932,11 +6589,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex;
align-items: center;
gap: 0.75rem;
+ flex-wrap: nowrap;
+ min-width: fit-content;
}
.rpg-view-toggle {
display: flex;
gap: 0.25rem;
+ flex-wrap: nowrap;
}
.rpg-view-btn {
@@ -5946,6 +6606,8 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
border-radius: 0.25rem;
color: var(--SmartThemeFastUISliderColColor);
cursor: pointer;
+ white-space: nowrap;
+ min-width: fit-content;
transition: all 0.2s ease;
font-size: 0.9rem;
display: flex;
@@ -5984,11 +6646,35 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
gap: 0;
background: var(--SmartThemeBlurTintColor);
border-bottom: 2px solid var(--SmartThemeBorderColor);
+ overflow-x: auto;
+ overflow-y: hidden;
+ flex-wrap: nowrap;
+ scrollbar-width: thin;
+ scrollbar-color: var(--SmartThemeBorderColor) transparent;
+}
+
+.rpg-tabs-nav::-webkit-scrollbar {
+ height: 6px;
+}
+
+.rpg-tabs-nav::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.rpg-tabs-nav::-webkit-scrollbar-thumb {
+ background: var(--SmartThemeBorderColor);
+ border-radius: 3px;
+}
+
+.rpg-tabs-nav::-webkit-scrollbar-thumb:hover {
+ background: var(--rpg-highlight);
}
/* Desktop tab button */
.rpg-tab-btn {
flex: 1;
+ min-width: fit-content;
+ white-space: nowrap;
padding: 0.75rem 1rem;
display: flex;
align-items: center;
@@ -6089,238 +6775,305 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
}
}
-/* ===================================================================
- Debug Panel Styles - Mobile-Friendly Debug Log Viewer
- =================================================================== */
-
/* ============================================
- DEBUG TOGGLE FAB BUTTON (Same pattern as mobile FABs)
+ QUESTS SYSTEM STYLING
============================================ */
-.rpg-debug-toggle {
- display: none; /* Hidden by default, shown when debugMode is enabled */
- align-items: center;
- justify-content: center;
- position: fixed;
- /* Position set by JavaScript based on saved settings */
- width: 44px;
- height: 44px;
- border-radius: 50%;
- background: var(--SmartThemeBlurTintColor);
- border: 2px solid var(--SmartThemeBorderColor);
- color: var(--rpg-text, #ecf0f1);
- font-size: 1.85vw;
- cursor: grab;
- z-index: 1000; /* Below refresh (1001) and mobile toggle (10002) */
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
- transition: opacity 0.3s ease, transform 0.2s ease, top 0.3s ease, left 0.3s ease, right 0.3s ease, bottom 0.3s ease;
- user-select: none;
- -webkit-user-select: none;
- will-change: top, left;
-}
-/* Disable transitions while actively dragging */
-.rpg-debug-toggle.dragging {
- transition: none;
- cursor: grabbing;
-}
-
-.rpg-debug-toggle:hover {
- transform: scale(1.1);
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
-}
-
-.rpg-debug-toggle:active {
- transform: scale(0.95);
-}
-
-/* Debug panel */
-.rpg-debug-panel {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- height: 60vh;
- background: var(--SmartThemeBlurTintColor, #1a1a1a);
- border-top: 2px solid var(--SmartThemeBorderColor, #333);
- z-index: 10002;
+/* Quest Container */
+#rpg-quests {
display: flex;
flex-direction: column;
- transform: translateY(100%);
- transition: transform 0.3s ease;
- box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.5);
+ gap: 1rem;
+ padding: 0.5rem;
+ font-size: 0.9rem;
}
-.rpg-debug-panel.rpg-debug-open {
- transform: translateY(0);
+/* Quest Sub-tabs Navigation (Main / Optional) */
+.rpg-quests-subtabs {
+ display: flex;
+ gap: 0.5rem;
+ border-bottom: 2px solid var(--SmartThemeBorderColor);
+ padding-bottom: 0.5rem;
}
-.rpg-debug-header {
+.rpg-quests-subtab {
+ flex: 1;
+ padding: 0.5rem 1rem;
+ background: transparent;
+ border: 2px solid var(--SmartThemeBorderColor);
+ border-radius: 0.25rem;
+ color: var(--SmartThemeBodyColor);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-weight: 500;
+ text-align: center;
+}
+
+.rpg-quests-subtab:hover {
+ background: rgba(var(--rpg-highlight-rgb, 233, 69, 96), 0.1);
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
+}
+
+.rpg-quests-subtab.active {
+ background: transparent;
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
+ font-weight: 600;
+}
+
+/* Quest Sections */
+.rpg-quest-section {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+.rpg-quest-header {
display: flex;
justify-content: space-between;
align-items: center;
- padding: 1rem;
- border-bottom: 1px solid var(--SmartThemeBorderColor, #333);
- background: var(--SmartThemeBodyColor, #0d0d0d);
+ padding-bottom: 0.5rem;
+ border-bottom: 1px solid var(--SmartThemeBorderColor);
}
-.rpg-debug-header h3 {
+.rpg-quest-header h4 {
margin: 0;
- font-size: 1.2rem;
- color: var(--rpg-text, #ecf0f1);
+ font-size: 1.1rem;
+ color: var(--SmartThemeBodyColor);
}
-.rpg-debug-actions {
+.rpg-quest-content {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+}
+
+/* Main Quest Display */
+.rpg-main-quest-display {
+ padding: 0.75rem;
+ background: var(--SmartThemeBlurTintColor);
+ border: 2px solid var(--rpg-highlight);
+ border-radius: 0.25rem;
+ color: var(--SmartThemeBodyColor);
+ line-height: 1.6;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ font-size: 1rem;
+ font-weight: 500;
+}
+
+.rpg-main-quest-actions {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: flex-end;
+}
+
+/* Optional Quests List */
+.rpg-quest-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.rpg-quest-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem;
+ background: var(--SmartThemeBlurTintColor);
+ border: 1px solid var(--SmartThemeBorderColor);
+ border-radius: 0.25rem;
+ transition: all 0.2s ease;
+}
+
+.rpg-quest-item:hover {
+ border-color: var(--rpg-highlight);
+ background: rgba(var(--rpg-highlight-rgb, 233, 69, 96), 0.05);
+}
+
+.rpg-quest-title {
+ flex: 1;
+ color: var(--SmartThemeBodyColor);
+ line-height: 1.5;
+ word-wrap: break-word;
+}
+
+.rpg-quest-actions {
display: flex;
gap: 0.5rem;
}
-.rpg-debug-actions button {
- background: var(--SmartThemeBlurTintColor, #2a2a2a);
- border: 1px solid var(--SmartThemeBorderColor, #444);
- color: var(--rpg-text, #ecf0f1);
- width: 36px;
- height: 36px;
- border-radius: 6px;
+/* Quest Buttons */
+.rpg-quest-edit,
+.rpg-quest-remove,
+.rpg-add-quest-btn {
+ padding: 0.4rem 0.75rem;
+ border: 1px solid var(--SmartThemeBorderColor);
+ border-radius: 0.25rem;
+ background: var(--SmartThemeBlurTintColor);
+ color: var(--SmartThemeBodyColor);
cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 0.85rem;
display: flex;
align-items: center;
- justify-content: center;
- transition: all 0.2s ease;
+ gap: 0.35rem;
}
-.rpg-debug-actions button:hover {
- background: var(--SmartThemeBorderColor, #333);
- transform: scale(1.05);
+.rpg-quest-edit:hover {
+ background: var(--ac-style-color-matchedText);
+ border-color: var(--ac-style-color-matchedText);
+ color: white;
}
-.rpg-debug-actions button:active {
- transform: scale(0.95);
+.rpg-quest-remove:hover {
+ background: #e74c3c;
+ border-color: #e74c3c;
+ color: white;
}
-.rpg-debug-actions button i {
- pointer-events: none; /* Prevent icon from blocking clicks */
+.rpg-add-quest-btn {
+ background: transparent;
+ border-color: var(--rpg-highlight);
+ color: var(--rpg-highlight);
}
-.rpg-debug-logs {
- flex: 1;
- overflow-y: auto;
- padding: 1rem;
- font-family: 'Courier New', Courier, monospace;
- font-size: 0.85rem;
- line-height: 1.4;
- color: var(--rpg-text, #ecf0f1);
+.rpg-add-quest-btn:hover {
+ background: rgba(var(--rpg-highlight-rgb, 233, 69, 96), 0.1);
+ border-color: var(--rpg-highlight);
}
-.rpg-debug-empty {
- text-align: center;
+/* Quest Empty State */
+.rpg-quest-empty {
padding: 2rem;
- color: #888;
+ text-align: center;
+ color: var(--SmartThemeFastUISliderColColor);
font-style: italic;
}
-.rpg-debug-entry {
- margin-bottom: 1rem;
- padding-bottom: 1rem;
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+/* Quest Hint */
+.rpg-quest-hint {
+ padding: 0.5rem;
+ background: transparent;
+ border: 2px solid var(--rpg-highlight);
+ border-radius: 0.25rem;
+ font-size: 0.85rem;
+ color: var(--SmartThemeFastUISliderColColor);
+ display: flex;
+ gap: 0.5rem;
+ align-items: flex-start;
}
-.rpg-debug-entry:last-child {
- border-bottom: none;
+.rpg-quest-hint i {
+ margin-top: 0.1rem;
}
-.rpg-debug-time {
- color: #888;
- font-size: 0.75rem;
-}
-
-.rpg-debug-message {
- color: #4fc3f7;
-}
-
-.rpg-debug-data {
- margin: 0.5rem 0 0 0;
+/* Quest Inline Edit Form */
+.rpg-quest-edit-form {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
padding: 0.75rem;
+ background: var(--SmartThemeBlurTintColor);
+ border: 2px solid var(--rpg-highlight);
+ border-radius: 0.25rem;
+}
+
+.rpg-quest-edit-form input,
+.rpg-quest-edit-form textarea {
+ width: 100%;
+ padding: 0.5rem;
background: rgba(0, 0, 0, 0.3);
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 4px;
- overflow-x: auto;
- color: #9ccc65;
- font-size: 0.8rem;
- white-space: pre-wrap;
- word-break: break-word;
+ border: 1px solid var(--SmartThemeBorderColor);
+ border-radius: 0.25rem;
+ color: var(--SmartThemeBodyColor);
+ font-family: inherit;
+ font-size: 0.9rem;
}
-/* Mobile view - slide from right like main panel */
-@media (max-width: 1000px) {
- .rpg-debug-panel {
- /* Reset bottom slide positioning */
- transform: none;
- transition: none;
- bottom: auto;
+.rpg-quest-edit-form input:focus,
+.rpg-quest-edit-form textarea:focus {
+ outline: none;
+ border-color: var(--rpg-highlight);
+}
- /* Mobile panel - slide from right */
- position: fixed !important;
- top: var(--topBarBlockSize) !important;
- right: 0 !important;
- left: auto !important;
+.rpg-quest-edit-actions {
+ display: flex;
+ gap: 0.5rem;
+ justify-content: flex-end;
+}
- /* Mobile sizing using dynamic viewport units */
- width: 85dvw !important;
- max-width: 400px !important;
- height: calc(100dvh - var(--topBarBlockSize)) !important;
+.rpg-quest-save,
+.rpg-quest-cancel {
+ padding: 0.5rem 1rem;
+ border: 1px solid var(--SmartThemeBorderColor);
+ border-radius: 0.25rem;
+ cursor: pointer;
+ font-size: 0.85rem;
+ transition: all 0.2s ease;
+}
- /* Hidden by default */
- display: none !important;
+.rpg-quest-save {
+ background: var(--rpg-highlight);
+ border-color: var(--rpg-highlight);
+ color: white;
+}
- /* Mobile scrolling */
- overflow-y: auto !important;
- -webkit-overflow-scrolling: touch;
+.rpg-quest-save:hover {
+ background: #ff6b81;
+ border-color: #ff6b81;
+}
- /* Styling */
- border-radius: 20px 0 0 0;
- border-left: 1px solid var(--SmartThemeBorderColor);
- border-top: 1px solid var(--SmartThemeBorderColor);
- border-bottom: none;
- backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
- box-shadow: -5px 0 20px rgba(0, 0, 0, 0.5);
+.rpg-quest-cancel {
+ background: transparent;
+ color: var(--SmartThemeBodyColor);
+}
+
+.rpg-quest-cancel:hover {
+ background: rgba(255, 255, 255, 0.1);
+}
+
+/* Mobile Responsive Styles */
+@media (max-width: 768px) {
+ .rpg-quests-subtabs {
+ flex-direction: column;
+ gap: 0.5rem;
}
- /* Show panel when opened with slide-in animation */
- .rpg-debug-panel.rpg-mobile-open {
- display: flex !important;
- z-index: 10002;
- animation: rpgSlideInFromRight 0.3s ease-in-out;
+ .rpg-quests-subtab {
+ padding: 0.75rem;
+ font-size: 1rem;
}
- /* Closing animation - slide out to right */
- .rpg-debug-panel.rpg-mobile-closing {
- display: flex !important;
- z-index: 10002;
- animation: rpgSlideOutToRight 0.3s ease-in-out;
+ .rpg-quest-header h4 {
+ font-size: 1rem;
}
- /* Debug logs container needs to stay scrollable */
- .rpg-debug-logs {
- overflow-y: auto;
- -webkit-overflow-scrolling: touch;
+ .rpg-quest-edit,
+ .rpg-quest-remove,
+ .rpg-add-quest-btn {
+ padding: 0.6rem 1rem;
+ font-size: 0.95rem;
+ min-height: 2.5rem;
}
- /* Debug toggle button on mobile */
- .rpg-debug-toggle {
- font-size: clamp(20px, 5.1vw, 26px) !important;
+ .rpg-quest-item {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .rpg-quest-actions {
+ width: 100%;
+ justify-content: space-between;
+ }
+
+ .rpg-main-quest-actions {
+ flex-direction: column;
+ }
+
+ .rpg-main-quest-actions button {
+ width: 100%;
}
}
-/* Desktop view - smaller panel in bottom right */
-@media (min-width: 1001px) {
- .rpg-debug-panel {
- bottom: 20px;
- left: auto;
- right: 20px;
- width: 600px;
- max-width: 90vw;
- height: 400px;
- border-radius: 12px;
- border: 2px solid var(--SmartThemeBorderColor, #333);
- }
-}
diff --git a/template.html b/template.html
index 2d3bac8..014e796 100644
--- a/template.html
+++ b/template.html
@@ -52,6 +52,11 @@
+
+
+
+
+