From 7e9d98738fccbbf3ec2c1cfe23157d9482bbad46 Mon Sep 17 00:00:00 2001 From: Spicy_Marinara Date: Thu, 8 Jan 2026 10:35:54 +0100 Subject: [PATCH] v3.2.0: Major update with JSON trackers, locking system, and UI improvements --- README.md | 14 +++--- index.js | 4 ++ manifest.json | 2 +- settings.html | 2 +- src/i18n/en.json | 12 ++--- src/i18n/zh-tw.json | 6 +-- src/systems/features/classicStats.js | 2 +- src/systems/features/jsonCleaning.js | 64 ++++++++++++++++++++------ src/systems/generation/injector.js | 10 ++-- src/systems/integration/sillytavern.js | 10 ++-- src/systems/ui/layout.js | 4 +- src/systems/ui/trackerEditor.js | 64 ++++++++++++++++++++++++-- style.css | 40 ++++++++++++---- template.html | 52 ++++++++++++--------- 14 files changed, 206 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 9f0c749..d49cefc 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,15 @@ An immersive RPG extension for browsers that tracks character stats, scene infor ## 🆕 What's New -### v3.1.1 +### v3.2.0 -- Mobile UI fixes. - -### v3.0.1 - -- Small bug fix where you couldn't edit the thought bubble. +- Mobile UI fixes (AGAIN). +- Fixed the regex issue in Together mode. +- Fixed the parsing error to not appear upon loading new chats or switching them. +- Fixed adding new relationships in the Edit Trackers. +- Added migration for importing older trackers' presets. +- Lifted attributes' cap to 999. +- Fixed some mobile displays. **Special thanks to all the other contributors for this project:** Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis. diff --git a/index.js b/index.js index 7cd0a81..f267039 100644 --- a/index.js +++ b/index.js @@ -315,6 +315,7 @@ async function initUI() { updateGenerationModeUI(); // Add or remove JSON cleaning regex based on mode + // This ensures old messages in chat history are also cleaned try { if (extensionSettings.generationMode === 'together') { await ensureJsonCleaningRegex(st_extension_settings, saveSettingsDebounced); @@ -1000,7 +1001,10 @@ jQuery(async () => { } // Import the JSON cleaning regex for Together mode if enabled + // This cleans historical messages when displayed + // Note: We also clean directly in message handler for redundancy try { + console.log('[RPG Companion] Checking JSON cleaning regex. Generation mode:', extensionSettings.generationMode); if (extensionSettings.generationMode === 'together') { await ensureJsonCleaningRegex(st_extension_settings, saveSettingsDebounced); } else { diff --git a/manifest.json b/manifest.json index 9d238a3..96d6d14 100644 --- a/manifest.json +++ b/manifest.json @@ -6,6 +6,6 @@ "js": "index.js", "css": "style.css", "author": "Marinara", - "version": "3.1.0", + "version": "3.2.0", "homePage": "https://github.com/SpicyMarinara/rpg-companion-sillytavern" } diff --git a/settings.html b/settings.html index ae85ae7..803e736 100644 --- a/settings.html +++ b/settings.html @@ -48,7 +48,7 @@
- v3.1.1 + v3.2.0
diff --git a/src/i18n/en.json b/src/i18n/en.json index 270fe8e..661f7b7 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -111,11 +111,11 @@ "template.trackerEditorModal.tabs.userStats": "User Stats", "template.trackerEditorModal.tabs.infoBox": "Info Box", "template.trackerEditorModal.tabs.presentCharacters": "Present Characters", - "template.trackerEditorModal.buttons.reset": "Reset to Defaults", + "template.trackerEditorModal.buttons.reset": "Reset", "template.trackerEditorModal.buttons.cancel": "Cancel", "template.trackerEditorModal.buttons.save": "Save & Apply", - "template.trackerEditorModal.buttons.export": "Export Preset", - "template.trackerEditorModal.buttons.import": "Import Preset", + "template.trackerEditorModal.buttons.export": "Export", + "template.trackerEditorModal.buttons.import": "Import", "template.trackerEditorModal.messages.exportSuccess": "Tracker preset exported successfully!", "template.trackerEditorModal.messages.exportError": "Failed to export tracker preset. Check console for details.", "template.trackerEditorModal.messages.importSuccess": "Tracker preset imported successfully!", @@ -145,10 +145,10 @@ "template.trackerEditorModal.infoBoxTab.recentEventsWidget": "Recent Events", "template.trackerEditorModal.presentCharactersTab.relationshipStatusTitle": "Relationship Status Fields", "template.trackerEditorModal.presentCharactersTab.enableRelationshipStatus": "Enable Relationship Status Fields", - "template.trackerEditorModal.presentCharactersTab.relationshipStatusHint": "Define relationship types with corresponding emojis shown on character portraits", + "template.trackerEditorModal.presentCharactersTab.relationshipStatusHint": "Define relationship types with corresponding emojis shown on character portraits.", "template.trackerEditorModal.presentCharactersTab.newRelationshipButton": "New Relationship", "template.trackerEditorModal.presentCharactersTab.appearanceDemeanorTitle": "Appearance/Demeanor Fields", - "template.trackerEditorModal.presentCharactersTab.appearanceDemeanorHint": "Fields shown below character name, separated by |", + "template.trackerEditorModal.presentCharactersTab.appearanceDemeanorHint": "Fields shown below character name.", "template.trackerEditorModal.presentCharactersTab.addCustomFieldButton": "Add Custom Field", "template.trackerEditorModal.presentCharactersTab.thoughtsConfigTitle": "Thoughts Configuration", "template.trackerEditorModal.presentCharactersTab.enableCharacterThoughts": "Enable Character Thoughts", @@ -156,7 +156,7 @@ "template.trackerEditorModal.presentCharactersTab.aiInstructionLabel": "AI Instruction:", "template.trackerEditorModal.presentCharactersTab.characterStatsTitle": "Character Stats", "template.trackerEditorModal.presentCharactersTab.trackCharacterStats": "Track Character Stats", - "template.trackerEditorModal.presentCharactersTab.characterStatsHint": "Create stats to track for each character (displayed as colored bars)", + "template.trackerEditorModal.presentCharactersTab.characterStatsHint": "Create stats to track for each character (displayed as colored bars).", "template.trackerEditorModal.presentCharactersTab.addCharacterStatButton": "Add Character Stat", "template.mainPanel.title": "RPG Companion", "template.mainPanel.lastRoll": "Last Roll:", diff --git a/src/i18n/zh-tw.json b/src/i18n/zh-tw.json index 51b5e79..05597c7 100644 --- a/src/i18n/zh-tw.json +++ b/src/i18n/zh-tw.json @@ -81,11 +81,11 @@ "template.trackerEditorModal.tabs.userStats": "User 屬性", "template.trackerEditorModal.tabs.infoBox": "資訊框", "template.trackerEditorModal.tabs.presentCharacters": "在場角色", - "template.trackerEditorModal.buttons.reset": "重置為預設值", + "template.trackerEditorModal.buttons.reset": "重置", "template.trackerEditorModal.buttons.cancel": "取消", "template.trackerEditorModal.buttons.save": "保存並應用", - "template.trackerEditorModal.buttons.export": "匯出預設", - "template.trackerEditorModal.buttons.import": "匯入預設", + "template.trackerEditorModal.buttons.export": "匯出", + "template.trackerEditorModal.buttons.import": "匯入", "template.trackerEditorModal.messages.exportSuccess": "追蹤器預設匯出成功!", "template.trackerEditorModal.messages.exportError": "匯出追蹤器預設失敗。請檢查控制台以獲取詳細資訊。", "template.trackerEditorModal.messages.importSuccess": "追蹤器預設匯入成功!", diff --git a/src/systems/features/classicStats.js b/src/systems/features/classicStats.js index 281f1c4..46337fa 100644 --- a/src/systems/features/classicStats.js +++ b/src/systems/features/classicStats.js @@ -19,7 +19,7 @@ export function setupClassicStatsButtons() { // Delegated event listener for increase buttons $userStatsContainer.on('click', '.rpg-stat-increase', function() { const stat = $(this).data('stat'); - if (extensionSettings.classicStats[stat] < 100) { + if (extensionSettings.classicStats[stat] < 999) { extensionSettings.classicStats[stat]++; saveSettings(); saveChatData(); diff --git a/src/systems/features/jsonCleaning.js b/src/systems/features/jsonCleaning.js index e8721c4..1305b2b 100644 --- a/src/systems/features/jsonCleaning.js +++ b/src/systems/features/jsonCleaning.js @@ -27,17 +27,52 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting st_extension_settings.regex = []; } - const alreadyExists = existingScripts.some(script => + const existingScript = existingScripts.find(script => script && script.scriptName && script.scriptName === scriptName ); - if (alreadyExists) { - // console.log('[RPG Companion] JSON cleaning regex already exists, skipping import'); + if (existingScript) { + // Update existing script with new regex pattern if it's different + const newPattern = '/```json[\\s\\S]*?```/gim'; + + if (existingScript.findRegex !== newPattern) { + existingScript.findRegex = newPattern; + existingScript.placement = [2]; // 2 = AI Output + existingScript.disabled = false; // Ensure it's enabled + existingScript.runOnEdit = true; // Ensure it runs on edit + existingScript.displayOnly = true; // Alter Chat Display + existingScript.onlyFormatDisplay = true; // Ensure display formatting is enabled + + if (typeof saveSettingsDebounced === 'function') { + saveSettingsDebounced(); + } + console.log('[RPG Companion] Updated regex pattern and placement.'); + } else { + // Ensure it's enabled even if pattern matches + if (existingScript.disabled) { + existingScript.disabled = false; + if (typeof saveSettingsDebounced === 'function') { + saveSettingsDebounced(); + } + console.log('[RPG Companion] Re-enabled disabled regex.'); + } else { + // Also update placement if it's wrong + const expectedPlacement = [2]; // 2 = AI Output + if (JSON.stringify(existingScript.placement) !== JSON.stringify(expectedPlacement)) { + existingScript.placement = expectedPlacement; + if (typeof saveSettingsDebounced === 'function') { + saveSettingsDebounced(); + // Force immediate save after a short delay + setTimeout(() => saveSettingsDebounced(), 100); + } + console.log('[RPG Companion] Updated regex placement to [2].'); + } + } + } + console.log('[RPG Companion] JSON Cleaning Regex is already downloaded and active.'); return; } - // console.log('[RPG Companion] Importing JSON cleaning regex for Together mode...'); - // Generate a UUID for the script const uuidv4 = () => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { @@ -50,19 +85,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 // 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 (non-greedy, multiline) - // This is now the guaranteed format since prompts instruct models to use code blocks - findRegex: '/```json\\s*[\\s\\S]*?```/gi', + // Match ```json...``` code blocks (handles spaces, newlines, any content) + // Using a more permissive pattern to catch all variations + findRegex: '/```json[\\s\\S]*?```/gim', replaceString: '', trimStrings: [], - placement: [0], // 0 = Output (transforms after generation, before display) + placement: [2], // 2 = AI Output disabled: false, markdownOnly: false, - promptOnly: false, // Apply to both prompts and outputs + promptOnly: false, runOnEdit: true, + displayOnly: true, // Alter Chat Display + onlyFormatDisplay: true, // Ensure display formatting is enabled substituteRegex: 0, minDepth: null, maxDepth: null @@ -74,6 +112,7 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting } st_extension_settings.regex.push(regexScript); + console.log('[RPG Companion] JSON Cleaning Regex created and activated.'); // Save the changes if (typeof saveSettingsDebounced === 'function') { @@ -81,11 +120,8 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting } else { console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save JSON cleaning regex'); } - - // console.log('[RPG Companion] ✅ JSON cleaning regex imported successfully'); - // console.log('[RPG Companion] This regex will automatically remove tracker JSON from Together mode messages'); } catch (error) { - console.error('[RPG Companion] Failed to import JSON cleaning regex:', error); + console.error('[RPG Companion] JSON Cleaning Regex failed to properly initialize!'); console.error('[RPG Companion] Error details:', error.message, error.stack); // Don't throw - continue without it } diff --git a/src/systems/generation/injector.js b/src/systems/generation/injector.js index a524cb6..bfbbed0 100644 --- a/src/systems/generation/injector.js +++ b/src/systems/generation/injector.js @@ -295,8 +295,8 @@ export async function onGenerationStarted(type, data, dryRun) { // Clear Spotify prompt if disabled setExtensionPrompt('rpg-companion-spotify', '', extension_prompt_types.IN_CHAT, 0, false); } - } else if (extensionSettings.generationMode === 'separate') { - // In SEPARATE mode, inject the contextual summary for main roleplay generation + } else if (extensionSettings.generationMode === 'separate' || extensionSettings.generationMode === 'external') { + // In SEPARATE and EXTERNAL modes, inject the contextual summary for main roleplay generation const contextSummary = generateContextualSummary(); if (contextSummary) { @@ -312,7 +312,7 @@ Ensure these details naturally reflect and influence the narrative. Character be if (!shouldSuppress) { setExtensionPrompt('rpg-companion-context', wrappedContext, extension_prompt_types.IN_CHAT, 1, false); } - // console.log('[RPG Companion] Injected contextual summary for separate mode:', contextSummary); + // console.log('[RPG Companion] Injected contextual summary for separate/external mode:', contextSummary); } else { // Clear if no data yet setExtensionPrompt('rpg-companion-context', '', extension_prompt_types.IN_CHAT, 1, false); @@ -325,7 +325,7 @@ Ensure these details naturally reflect and influence the narrative. Character be const htmlPrompt = `\n${htmlPromptText}`; setExtensionPrompt('rpg-companion-html', htmlPrompt, extension_prompt_types.IN_CHAT, 0, false); - // console.log('[RPG Companion] Injected HTML prompt at depth 0 for separate mode'); + // console.log('[RPG Companion] Injected HTML prompt at depth 0 for separate/external mode'); } else { // Clear HTML prompt if disabled setExtensionPrompt('rpg-companion-html', '', extension_prompt_types.IN_CHAT, 0, false); @@ -338,7 +338,7 @@ Ensure these details naturally reflect and influence the narrative. Character be const spotifyPrompt = `\n${spotifyPromptText} ${SPOTIFY_FORMAT_INSTRUCTION}`; setExtensionPrompt('rpg-companion-spotify', spotifyPrompt, extension_prompt_types.IN_CHAT, 0, false); - // console.log('[RPG Companion] Injected Spotify prompt at depth 0 for separate mode'); + // console.log('[RPG Companion] Injected Spotify prompt at depth 0 for separate/external mode'); } else { // Clear Spotify prompt if disabled setExtensionPrompt('rpg-companion-spotify', '', extension_prompt_types.IN_CHAT, 0, false); diff --git a/src/systems/integration/sillytavern.js b/src/systems/integration/sillytavern.js index 81974bb..33fea8a 100644 --- a/src/systems/integration/sillytavern.js +++ b/src/systems/integration/sillytavern.js @@ -140,10 +140,8 @@ export async function onMessageReceived(data) { const responseText = lastMessage.mes; const parsedData = parseResponse(responseText); - // Check if parsing completely failed (no tracker data found) - if (parsedData.parsingFailed) { - toastr.error(i18n.getTranslation('errors.parsingError'), '', { timeOut: 5000 }); - } + // Note: Don't show parsing error here - this event fires when loading chat history too + // Error notification is handled in apiClient.js for fresh generations only // Remove locks from parsed data (JSON format only, text format is unaffected) if (parsedData.userStats) { @@ -195,7 +193,9 @@ export async function onMessageReceived(data) { // Only remove trackers if saveTrackerHistory is disabled // When enabled, trackers are in XML tags which SillyTavern auto-hides if (!extensionSettings.saveTrackerHistory) { - // Remove all code blocks that contain tracker data + // Note: JSON code blocks are hidden from display by regex script (but preserved in message data) + + // Remove old text format code blocks (legacy support) cleanedMessage = cleanedMessage.replace(/```[^`]*?Stats\s*\n\s*---[^`]*?```\s*/gi, ''); cleanedMessage = cleanedMessage.replace(/```[^`]*?Info Box\s*\n\s*---[^`]*?```\s*/gi, ''); cleanedMessage = cleanedMessage.replace(/```[^`]*?Present Characters\s*\n\s*---[^`]*?```\s*/gi, ''); diff --git a/src/systems/ui/layout.js b/src/systems/ui/layout.js index 5e2f827..2b3e70c 100644 --- a/src/systems/ui/layout.js +++ b/src/systems/ui/layout.js @@ -443,10 +443,10 @@ export function updateGenerationModeUI() { // Show auto-update toggle $('#rpg-auto-update-container').slideDown(200); } else if (extensionSettings.generationMode === 'external') { - // In "external" mode, manual update button is visible AND external settings are shown + // In "external" mode, manual update button is visible AND both settings are shown $('#rpg-manual-update').show(); $('#rpg-external-api-settings').slideDown(200); - $('#rpg-separate-mode-settings').slideUp(200); + $('#rpg-separate-mode-settings').slideDown(200); // Show auto-update toggle for external mode too $('#rpg-auto-update-container').slideDown(200); } diff --git a/src/systems/ui/trackerEditor.js b/src/systems/ui/trackerEditor.js index f51a47e..9ea4b32 100644 --- a/src/systems/ui/trackerEditor.js +++ b/src/systems/ui/trackerEditor.js @@ -251,6 +251,48 @@ function exportTrackerPreset() { } } +/** + * Migrates old tracker preset format to current format + * @param {Object} config - The tracker config to migrate + * @returns {Object} - Migrated tracker config + */ +function migrateTrackerPreset(config) { + // Create a deep copy to avoid modifying the original + const migrated = JSON.parse(JSON.stringify(config)); + + // Migrate relationships structure (v3.0.0 -> v3.1.0) + if (migrated.presentCharacters) { + // Old format: relationshipEmojis directly on presentCharacters + // New format: relationships.relationshipEmojis + if (migrated.presentCharacters.relationshipEmojis && + !migrated.presentCharacters.relationships) { + migrated.presentCharacters.relationships = { + enabled: migrated.presentCharacters.enableRelationships || true, + relationshipEmojis: migrated.presentCharacters.relationshipEmojis + }; + // Keep legacy fields for backward compatibility + migrated.presentCharacters.relationshipFields = Object.keys(migrated.presentCharacters.relationshipEmojis); + } + + // Ensure relationships object exists + if (!migrated.presentCharacters.relationships) { + migrated.presentCharacters.relationships = { + enabled: false, + relationshipEmojis: {} + }; + } + + // Ensure relationshipEmojis exists within relationships + if (!migrated.presentCharacters.relationships.relationshipEmojis) { + migrated.presentCharacters.relationships.relationshipEmojis = {}; + } + } + + // Add any other migration logic here for future format changes + + return migrated; +} + /** * Import tracker configuration from a JSON file */ @@ -278,6 +320,9 @@ function importTrackerPreset() { throw new Error('Invalid preset file: missing required configuration sections'); } + // Migrate old preset format to current format + const migratedConfig = migrateTrackerPreset(data.trackerConfig); + // Ask for confirmation const confirmMessage = i18n.getTranslation('template.trackerEditorModal.messages.importConfirm') || 'This will replace your current tracker configuration. Continue?'; @@ -286,8 +331,8 @@ function importTrackerPreset() { return; } - // Apply the imported configuration - extensionSettings.trackerConfig = JSON.parse(JSON.stringify(data.trackerConfig)); // Deep copy + // Apply the migrated configuration + extensionSettings.trackerConfig = migratedConfig; // Re-render the editor UI renderEditorUI(); @@ -794,14 +839,25 @@ function setupPresentCharactersListeners() { extensionSettings.trackerConfig.presentCharacters.relationships.relationshipEmojis = {}; } + // Generate a unique relationship name + let baseName = 'New Relationship'; + let relationshipName = baseName; + let counter = 1; + const existingRelationships = extensionSettings.trackerConfig.presentCharacters.relationships.relationshipEmojis; + + while (existingRelationships[relationshipName]) { + counter++; + relationshipName = `${baseName} ${counter}`; + } + // Add to new structure - extensionSettings.trackerConfig.presentCharacters.relationships.relationshipEmojis['New Relationship'] = '😊'; + extensionSettings.trackerConfig.presentCharacters.relationships.relationshipEmojis[relationshipName] = '😊'; // Also update legacy fields for backward compatibility if (!extensionSettings.trackerConfig.presentCharacters.relationshipEmojis) { extensionSettings.trackerConfig.presentCharacters.relationshipEmojis = {}; } - extensionSettings.trackerConfig.presentCharacters.relationshipEmojis['New Relationship'] = '😊'; + extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[relationshipName] = '😊'; // Sync relationshipFields const emojis = extensionSettings.trackerConfig.presentCharacters.relationships.relationshipEmojis; diff --git a/style.css b/style.css index 3e8f703..42b2735 100644 --- a/style.css +++ b/style.css @@ -601,6 +601,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { flex: 1; overflow-y: auto; overflow-x: hidden; + position: relative; /* For absolute positioning of lock icon on mobile */ } /* Hide the stats header - we'll integrate portrait/inventory into stats content */ @@ -4231,6 +4232,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld { background: var(--rpg-accent); border: 1px solid var(--rpg-border); border-radius: 0.375em; + min-width: 0; /* Allow grid items to shrink below content size */ } .rpg-field-controls { @@ -4269,6 +4271,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld { border-radius: 0.25em; color: var(--rpg-text); font-size: 0.95em; + word-wrap: break-word; + overflow-wrap: break-word; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; /* Allow input to shrink */ + width: 100%; /* Fill available grid space */ + box-sizing: border-box; } .rpg-field-remove, @@ -4305,15 +4314,28 @@ body:has(.rpg-panel.rpg-position-left) #sheld { /* Footer buttons */ .rpg-settings-popup-footer { - display: flex; - justify-content: space-between; - align-items: center; padding: 1em; border-top: 2px solid var(--rpg-border); - gap: 1em; background: var(--rpg-accent); } +.rpg-editor-footer-buttons { + display: flex; + flex-direction: column; + gap: 0.75em; +} + +.rpg-editor-footer-row { + display: flex; + gap: 0.5em; + justify-content: center; +} + +.rpg-editor-footer-row:last-child button { + flex: 1; + max-width: 400px; +} + .rpg-footer-left { display: flex; gap: 0.5em; @@ -5116,15 +5138,15 @@ body:has(.rpg-panel.rpg-position-left) #sheld { /* Mobile panel - slide from right like desktop */ .rpg-panel { position: fixed !important; - top: var(--topBarBlockSize) !important; + top: 0 !important; right: 0 !important; bottom: 0 !important; left: auto !important; /* Mobile panel sizing */ - width: 85dvw !important; + width: 85vw !important; max-width: 400px !important; - height: calc(100dvh - var(--topBarBlockSize)) !important; + height: 100vh !important; /* Hidden by default - completely removed from layout */ display: none !important; @@ -5154,14 +5176,14 @@ body:has(.rpg-panel.rpg-position-left) #sheld { /* Show panel when opened with slide-in animation */ .rpg-panel.rpg-mobile-open { display: block !important; - z-index: 50; + z-index: 9999; animation: rpgSlideInFromRight 0.3s ease-in-out; } /* Closing animation - slide out to right */ .rpg-panel.rpg-mobile-closing { display: block !important; - z-index: 50; + z-index: 9999; animation: rpgSlideOutToRight 0.3s ease-in-out; } diff --git a/template.html b/template.html index 4d7b915..bcfd34a 100644 --- a/template.html +++ b/template.html @@ -492,7 +492,7 @@ data-i18n-key="template.settingsModal.advanced.contextMessages">Context Messages: Number of recent messages - to include (Separate mode only) + to include (Separate/External mode) @@ -694,27 +694,26 @@
- -
@@ -899,7 +898,7 @@

- Welcome to RPG Companion v.3.0.0! + Welcome to RPG Companion v.3.2.0!