Rename expression sync to thought-based expression portraits and clean up compatibility solutions

This commit is contained in:
Tremendoussly
2026-03-15 22:18:25 +01:00
parent c79c941871
commit 1a11699522
14 changed files with 201 additions and 218 deletions
+22 -22
View File
@@ -139,15 +139,15 @@ import {
renderAlternatePresentCharacters renderAlternatePresentCharacters
} from './src/systems/ui/alternatePresentCharacters.js'; } from './src/systems/ui/alternatePresentCharacters.js';
import { import {
initExpressionSync, initThoughtBasedExpressions,
queueExpressionSyncFromThoughts, queueThoughtBasedExpressionsUpdate,
onExpressionSyncSettingChanged, onThoughtBasedExpressionsSettingChanged,
onAlternatePresentCharactersVisibilityChanged, onAlternatePresentCharactersVisibilityChanged,
onHideDefaultExpressionDisplaySettingChanged, onHideDefaultExpressionDisplaySettingChanged,
clearExpressionSyncCache, clearThoughtBasedExpressionsCache,
onExpressionSyncChatChanged, onThoughtBasedExpressionsChatChanged,
setExpressionSyncRefreshHandler setThoughtBasedExpressionsRefreshHandler
} from './src/systems/integration/expressionSync.js'; } from './src/systems/integration/thoughtBasedExpressions.js';
// Feature modules // Feature modules
import { setupPlotButtons, sendPlotProgression } from './src/systems/features/plotProgression.js'; import { setupPlotButtons, sendPlotProgression } from './src/systems/features/plotProgression.js';
@@ -177,7 +177,7 @@ import {
// Old state variable declarations removed - now imported from core modules // Old state variable declarations removed - now imported from core modules
// (extensionSettings, lastGeneratedData, committedTrackerData, etc. are now in src/core/state.js) // (extensionSettings, lastGeneratedData, committedTrackerData, etc. are now in src/core/state.js)
setExpressionSyncRefreshHandler(() => { setThoughtBasedExpressionsRefreshHandler(() => {
renderAlternatePresentCharacters({ useCommittedFallback: true }); renderAlternatePresentCharacters({ useCommittedFallback: true });
}); });
@@ -237,7 +237,7 @@ async function addExtensionSettings() {
clearExtensionPrompts(); clearExtensionPrompts();
updateChatThoughts(); // Remove thought bubbles updateChatThoughts(); // Remove thought bubbles
cleanupCheckpointUI(); // Remove checkpoint buttons and indicators cleanupCheckpointUI(); // Remove checkpoint buttons and indicators
clearExpressionSyncCache(); clearThoughtBasedExpressionsCache();
// Disable dynamic weather effects // Disable dynamic weather effects
toggleDynamicWeather(false); toggleDynamicWeather(false);
@@ -253,7 +253,7 @@ async function addExtensionSettings() {
await initUI(); await initUI();
loadChatData(); // Load chat data for current chat loadChatData(); // Load chat data for current chat
scheduleChatStateRehydration(); scheduleChatStateRehydration();
initExpressionSync(); initThoughtBasedExpressions();
updateChatThoughts(); // Create thought bubbles if data exists updateChatThoughts(); // Create thought bubbles if data exists
injectCheckpointButton(); // Re-add checkpoint buttons injectCheckpointButton(); // Re-add checkpoint buttons
updateAllCheckpointIndicators(); // Update button states updateAllCheckpointIndicators(); // Update button states
@@ -370,10 +370,10 @@ async function initUI() {
onAlternatePresentCharactersVisibilityChanged(); onAlternatePresentCharactersVisibilityChanged();
}); });
$('#rpg-toggle-sync-expressions').on('change', function() { $('#rpg-toggle-thought-based-expressions').on('change', function() {
extensionSettings.syncExpressionsToPresentCharacters = $(this).prop('checked'); extensionSettings.enableThoughtBasedExpressions = $(this).prop('checked');
saveSettings(); saveSettings();
onExpressionSyncSettingChanged(extensionSettings.syncExpressionsToPresentCharacters); onThoughtBasedExpressionsSettingChanged(extensionSettings.enableThoughtBasedExpressions);
}); });
$('#rpg-toggle-hide-default-expressions').on('change', function() { $('#rpg-toggle-hide-default-expressions').on('change', function() {
@@ -1095,7 +1095,7 @@ async function initUI() {
$('#rpg-toggle-info-box').prop('checked', extensionSettings.showInfoBox); $('#rpg-toggle-info-box').prop('checked', extensionSettings.showInfoBox);
$('#rpg-toggle-thoughts').prop('checked', extensionSettings.showCharacterThoughts); $('#rpg-toggle-thoughts').prop('checked', extensionSettings.showCharacterThoughts);
$('#rpg-toggle-alt-present-characters').prop('checked', extensionSettings.showAlternatePresentCharactersPanel ?? false); $('#rpg-toggle-alt-present-characters').prop('checked', extensionSettings.showAlternatePresentCharactersPanel ?? false);
$('#rpg-toggle-sync-expressions').prop('checked', extensionSettings.syncExpressionsToPresentCharacters === true); $('#rpg-toggle-thought-based-expressions').prop('checked', extensionSettings.enableThoughtBasedExpressions === true);
$('#rpg-toggle-hide-default-expressions').prop('checked', extensionSettings.hideDefaultExpressionDisplay === true); $('#rpg-toggle-hide-default-expressions').prop('checked', extensionSettings.hideDefaultExpressionDisplay === true);
$('#rpg-toggle-inventory').prop('checked', extensionSettings.showInventory); $('#rpg-toggle-inventory').prop('checked', extensionSettings.showInventory);
$('#rpg-toggle-quests').prop('checked', extensionSettings.showQuests); $('#rpg-toggle-quests').prop('checked', extensionSettings.showQuests);
@@ -1339,7 +1339,7 @@ jQuery(async () => {
try { try {
loadChatData(); loadChatData();
scheduleChatStateRehydration(); scheduleChatStateRehydration();
initExpressionSync(); initThoughtBasedExpressions();
// Initialize FAB widgets and strip widgets with any loaded data // Initialize FAB widgets and strip widgets with any loaded data
updateFabWidgets(); updateFabWidgets();
updateStripWidgets(); updateStripWidgets();
@@ -1425,7 +1425,7 @@ jQuery(async () => {
const renderedMessage = chat[messageId]; const renderedMessage = chat[messageId];
if (renderedMessage && !renderedMessage.is_user && !renderedMessage.is_system) { if (renderedMessage && !renderedMessage.is_user && !renderedMessage.is_system) {
queueExpressionSyncFromThoughts(); queueThoughtBasedExpressionsUpdate();
} }
}); });
@@ -1436,7 +1436,7 @@ jQuery(async () => {
const updatedMessage = chat[messageId]; const updatedMessage = chat[messageId];
if (updatedMessage && !updatedMessage.is_user && !updatedMessage.is_system) { if (updatedMessage && !updatedMessage.is_user && !updatedMessage.is_system) {
queueExpressionSyncFromThoughts(); queueThoughtBasedExpressionsUpdate();
} }
}); });
@@ -1447,13 +1447,13 @@ jQuery(async () => {
const swipedMessage = chat[messageIndex]; const swipedMessage = chat[messageIndex];
if (swipedMessage && !swipedMessage.is_user && !swipedMessage.is_system) { if (swipedMessage && !swipedMessage.is_user && !swipedMessage.is_system) {
queueExpressionSyncFromThoughts({ immediate: true }); queueThoughtBasedExpressionsUpdate({ immediate: true });
} }
}); });
eventSource.on(event_types.CHAT_CHANGED, () => { eventSource.on(event_types.CHAT_CHANGED, () => {
clearExpressionSyncCache(); clearThoughtBasedExpressionsCache();
setTimeout(() => onExpressionSyncChatChanged(), 0); setTimeout(() => onThoughtBasedExpressionsChatChanged(), 0);
}); });
eventSource.on(event_types.MESSAGE_DELETED, () => { eventSource.on(event_types.MESSAGE_DELETED, () => {
@@ -1461,8 +1461,8 @@ jQuery(async () => {
return; return;
} }
clearExpressionSyncCache(); clearThoughtBasedExpressionsCache();
setTimeout(() => onExpressionSyncChatChanged(), 0); setTimeout(() => onThoughtBasedExpressionsChatChanged(), 0);
}); });
} catch (error) { } catch (error) {
console.error('[RPG Companion] Event registration failed:', error); console.error('[RPG Companion] Event registration failed:', error);
+1 -1
View File
@@ -30,7 +30,7 @@ export const defaultSettings = {
showInfoBox: true, showInfoBox: true,
showCharacterThoughts: true, showCharacterThoughts: true,
showAlternatePresentCharactersPanel: false, showAlternatePresentCharactersPanel: false,
syncExpressionsToPresentCharacters: false, enableThoughtBasedExpressions: false,
hideDefaultExpressionDisplay: false, hideDefaultExpressionDisplay: false,
showInventory: true, // Show inventory section (v2 system) showInventory: true, // Show inventory section (v2 system)
showQuests: true, // Show quests section showQuests: true, // Show quests section
+8 -13
View File
@@ -9,13 +9,13 @@ import {
extensionSettings, extensionSettings,
lastGeneratedData, lastGeneratedData,
committedTrackerData, committedTrackerData,
syncedExpressionPortraits, thoughtBasedExpressionPortraits,
setExtensionSettings, setExtensionSettings,
updateExtensionSettings, updateExtensionSettings,
setLastGeneratedData, setLastGeneratedData,
setCommittedTrackerData, setCommittedTrackerData,
setSyncedExpressionPortraits, setThoughtBasedExpressionPortraits,
clearSyncedExpressionPortraits, clearThoughtBasedExpressionPortraits,
FEATURE_FLAGS FEATURE_FLAGS
} from './state.js'; } from './state.js';
import { migrateInventory } from '../utils/migration.js'; import { migrateInventory } from '../utils/migration.js';
@@ -384,11 +384,6 @@ export function loadSettings() {
settingsChanged = true; settingsChanged = true;
} }
if (extensionSettings.syncExpressionsToPresentCharacters === undefined) {
extensionSettings.syncExpressionsToPresentCharacters = false;
settingsChanged = true;
}
if (extensionSettings.hideDefaultExpressionDisplay === undefined) { if (extensionSettings.hideDefaultExpressionDisplay === undefined) {
extensionSettings.hideDefaultExpressionDisplay = false; extensionSettings.hideDefaultExpressionDisplay = false;
settingsChanged = true; settingsChanged = true;
@@ -478,7 +473,7 @@ export function saveChatData() {
quests: extensionSettings.quests, quests: extensionSettings.quests,
lastGeneratedData: lastGeneratedData, lastGeneratedData: lastGeneratedData,
committedTrackerData: committedTrackerData, committedTrackerData: committedTrackerData,
syncedExpressionPortraits: syncedExpressionPortraits, thoughtBasedExpressionPortraits: thoughtBasedExpressionPortraits,
timestamp: Date.now() timestamp: Date.now()
}; };
@@ -562,7 +557,7 @@ export function loadChatData() {
infoBox: null, infoBox: null,
characterThoughts: null characterThoughts: null
}); });
clearSyncedExpressionPortraits(); clearThoughtBasedExpressionPortraits();
} }
// Restore stats // Restore stats
@@ -611,10 +606,10 @@ export function loadChatData() {
// console.log('[RPG Companion] ⚠️ No lastGeneratedData found in save'); // console.log('[RPG Companion] ⚠️ No lastGeneratedData found in save');
} }
if (savedData?.syncedExpressionPortraits && typeof savedData.syncedExpressionPortraits === 'object') { if (savedData?.thoughtBasedExpressionPortraits && typeof savedData.thoughtBasedExpressionPortraits === 'object') {
setSyncedExpressionPortraits(savedData.syncedExpressionPortraits); setThoughtBasedExpressionPortraits(savedData.thoughtBasedExpressionPortraits);
} else { } else {
clearSyncedExpressionPortraits(); clearThoughtBasedExpressionPortraits();
} }
// Migrate inventory in chat data if feature flag enabled // Migrate inventory in chat data if feature flag enabled
+9 -25
View File
@@ -19,7 +19,7 @@ export let extensionSettings = {
showInfoBox: true, showInfoBox: true,
showCharacterThoughts: true, showCharacterThoughts: true,
showAlternatePresentCharactersPanel: false, showAlternatePresentCharactersPanel: false,
syncExpressionsToPresentCharacters: false, enableThoughtBasedExpressions: false,
hideDefaultExpressionDisplay: false, hideDefaultExpressionDisplay: false,
showInventory: true, // Show inventory section (v2 system) showInventory: true, // Show inventory section (v2 system)
showQuests: true, // Show quests section showQuests: true, // Show quests section
@@ -364,37 +364,21 @@ export function clearSessionAvatarPrompts() {
} }
/** /**
* Per-chat storage for thoughts-synced Character Expressions portraits. * Per-chat storage for thought-based Character Expressions portraits.
* Maps normalized character names to the current below-chat portrait URL. * Maps normalized character names to the current below-chat portrait URL.
*/ */
export let syncedExpressionPortraits = {}; export let thoughtBasedExpressionPortraits = {};
export function setSyncedExpressionPortrait(characterName, src) { export function setThoughtBasedExpressionPortraits(portraits) {
if (!characterName || !src) { thoughtBasedExpressionPortraits = portraits && typeof portraits === 'object' ? { ...portraits } : {};
return;
} }
syncedExpressionPortraits[characterName] = src; export function getThoughtBasedExpressionPortrait(characterName) {
return thoughtBasedExpressionPortraits[characterName] || null;
} }
export function removeSyncedExpressionPortrait(characterName) { export function clearThoughtBasedExpressionPortraits() {
if (!characterName) { thoughtBasedExpressionPortraits = {};
return;
}
delete syncedExpressionPortraits[characterName];
}
export function setSyncedExpressionPortraits(portraits) {
syncedExpressionPortraits = portraits && typeof portraits === 'object' ? { ...portraits } : {};
}
export function getSyncedExpressionPortrait(characterName) {
return syncedExpressionPortraits[characterName] || null;
}
export function clearSyncedExpressionPortraits() {
syncedExpressionPortraits = {};
} }
/** /**
+2 -2
View File
@@ -36,8 +36,8 @@
"template.settingsModal.display.showPresentCharactersNote": "Display character portraits with their current thoughts and status.", "template.settingsModal.display.showPresentCharactersNote": "Display character portraits with their current thoughts and status.",
"template.settingsModal.display.showBelowChatPresentCharacters": "Show Below-Chat Present Characters", "template.settingsModal.display.showBelowChatPresentCharacters": "Show Below-Chat Present Characters",
"template.settingsModal.display.showBelowChatPresentCharactersNote": "Display a compact Present Characters panel below the chat.", "template.settingsModal.display.showBelowChatPresentCharactersNote": "Display a compact Present Characters panel below the chat.",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressions": "Sync Expressions in Below-Chat Panel", "template.settingsModal.display.thoughtBasedExpressions": "Thought-Based Expressions",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressionsNote": "Use SillyTavern Character Expressions to classify each present character's thoughts for the below-chat panel. If Character Expressions uses Main API with Full Context, this may increase token usage.", "template.settingsModal.display.thoughtBasedExpressionsNote": "Use SillyTavern Character Expressions to classify each present character's thoughts for the below-chat panel. May increase token usage depending on the selected Classifier API.",
"template.settingsModal.display.hideDefaultExpressionDisplay": "Hide Default Expression Display", "template.settingsModal.display.hideDefaultExpressionDisplay": "Hide Default Expression Display",
"template.settingsModal.display.hideDefaultExpressionDisplayNote": "Hide SillyTavern's built-in Character Expressions display.", "template.settingsModal.display.hideDefaultExpressionDisplayNote": "Hide SillyTavern's built-in Character Expressions display.",
"template.settingsModal.display.narratorMode": "Narrator Mode", "template.settingsModal.display.narratorMode": "Narrator Mode",
+2 -2
View File
@@ -37,8 +37,8 @@
"template.settingsModal.display.showPresentCharactersNote": "Afficher les portraits des personnages avec leurs pensées actuelles et leur statut.", "template.settingsModal.display.showPresentCharactersNote": "Afficher les portraits des personnages avec leurs pensées actuelles et leur statut.",
"template.settingsModal.display.showBelowChatPresentCharacters": "Afficher les personnages sous le chat", "template.settingsModal.display.showBelowChatPresentCharacters": "Afficher les personnages sous le chat",
"template.settingsModal.display.showBelowChatPresentCharactersNote": "Afficher un panneau compact des personnages présents sous le chat.", "template.settingsModal.display.showBelowChatPresentCharactersNote": "Afficher un panneau compact des personnages présents sous le chat.",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressions": "Synchroniser les expressions dans le panneau sous le chat", "template.settingsModal.display.thoughtBasedExpressions": "Expressions basées sur les pensées",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressionsNote": "Utiliser Character Expressions de SillyTavern pour classifier les pensées de chaque personnage présent dans le panneau sous le chat. Si Character Expressions utilise Main API avec Full Context, cela peut augmenter l'utilisation de tokens.", "template.settingsModal.display.thoughtBasedExpressionsNote": "Utiliser Character Expressions de SillyTavern pour classifier les pensées de chaque personnage présent dans le panneau sous le chat. L'utilisation de tokens peut augmenter selon l'API de classification sélectionnée.",
"template.settingsModal.display.hideDefaultExpressionDisplay": "Masquer l'affichage d'expressions par défaut", "template.settingsModal.display.hideDefaultExpressionDisplay": "Masquer l'affichage d'expressions par défaut",
"template.settingsModal.display.hideDefaultExpressionDisplayNote": "Masquer l'affichage intégré des expressions de personnage de SillyTavern.", "template.settingsModal.display.hideDefaultExpressionDisplayNote": "Masquer l'affichage intégré des expressions de personnage de SillyTavern.",
"template.settingsModal.display.narratorMode": "Mode Narrateur", "template.settingsModal.display.narratorMode": "Mode Narrateur",
+2 -2
View File
@@ -36,8 +36,8 @@
"template.settingsModal.display.showPresentCharactersNote": "Показывать портреты персонажей с их текущимы мыслями и статусом.", "template.settingsModal.display.showPresentCharactersNote": "Показывать портреты персонажей с их текущимы мыслями и статусом.",
"template.settingsModal.display.showBelowChatPresentCharacters": "Показывать персонажей под чатом", "template.settingsModal.display.showBelowChatPresentCharacters": "Показывать персонажей под чатом",
"template.settingsModal.display.showBelowChatPresentCharactersNote": "Показывать компактную панель персонажей под чатом.", "template.settingsModal.display.showBelowChatPresentCharactersNote": "Показывать компактную панель персонажей под чатом.",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressions": "Синхронизировать выражения в панели под чатом", "template.settingsModal.display.thoughtBasedExpressions": "Выражения на основе мыслей",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressionsNote": "Использовать Character Expressions в SillyTavern для классификации мыслей каждого присутствующего персонажа в панели под чатом. Если Character Expressions использует Main API с Full Context, расход token-ов может вырасти.", "template.settingsModal.display.thoughtBasedExpressionsNote": "Использовать Character Expressions в SillyTavern для классификации мыслей каждого присутствующего персонажа в панели под чатом. Расход токенов может увеличиться в зависимости от выбранного API классификации.",
"template.settingsModal.display.hideDefaultExpressionDisplay": "Скрыть отображение выражений по умолчанию", "template.settingsModal.display.hideDefaultExpressionDisplay": "Скрыть отображение выражений по умолчанию",
"template.settingsModal.display.hideDefaultExpressionDisplayNote": "Скрыть встроенное отображение выражений персонажей SillyTavern.", "template.settingsModal.display.hideDefaultExpressionDisplayNote": "Скрыть встроенное отображение выражений персонажей SillyTavern.",
"template.settingsModal.display.narratorMode": "Режим расказчика", "template.settingsModal.display.narratorMode": "Режим расказчика",
+2 -2
View File
@@ -32,8 +32,8 @@
"template.settingsModal.display.showPresentCharacters": "顯示在場角色", "template.settingsModal.display.showPresentCharacters": "顯示在場角色",
"template.settingsModal.display.showBelowChatPresentCharacters": "顯示聊天下方的在場角色", "template.settingsModal.display.showBelowChatPresentCharacters": "顯示聊天下方的在場角色",
"template.settingsModal.display.showBelowChatPresentCharactersNote": "在聊天下方顯示精簡的在場角色面板。", "template.settingsModal.display.showBelowChatPresentCharactersNote": "在聊天下方顯示精簡的在場角色面板。",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressions": "在聊天下方面板同步表情", "template.settingsModal.display.thoughtBasedExpressions": "基於想法的表情",
"template.settingsModal.display.syncBelowChatPresentCharactersExpressionsNote": "使用 SillyTavern Character Expressions 對聊天下方面板中每個在場角色的想法進行分類。如果 Character Expressions 使用 Main API + Full Context,可能會增加 token 使用量。", "template.settingsModal.display.thoughtBasedExpressionsNote": "使用 SillyTavern Character Expressions 對聊天下方面板中每個在場角色的想法進行分類。Token 用量可能會依所選的分類 API 而增加。",
"template.settingsModal.display.hideDefaultExpressionDisplay": "隱藏預設表情顯示", "template.settingsModal.display.hideDefaultExpressionDisplay": "隱藏預設表情顯示",
"template.settingsModal.display.hideDefaultExpressionDisplayNote": "隱藏 SillyTavern 內建的角色表情顯示。", "template.settingsModal.display.hideDefaultExpressionDisplayNote": "隱藏 SillyTavern 內建的角色表情顯示。",
"template.settingsModal.display.showInventory": "顯示物品欄", "template.settingsModal.display.showInventory": "顯示物品欄",
@@ -1,7 +1,7 @@
/** /**
* Character Expressions -> below-chat Present Characters portrait sync. * Thought-based Character Expressions for the below-chat Present Characters panel.
* *
* Derives expression portraits from the current Present Characters thoughts * Derives portrait expressions from the current Present Characters thoughts
* payload, while keeping SillyTavern's native Character Expressions widget * payload, while keeping SillyTavern's native Character Expressions widget
* independent from the below-chat panel. * independent from the below-chat panel.
*/ */
@@ -9,15 +9,15 @@
import { getContext } from '../../../../../../extensions.js'; import { getContext } from '../../../../../../extensions.js';
import { import {
extensionSettings, extensionSettings,
syncedExpressionPortraits, thoughtBasedExpressionPortraits,
setSyncedExpressionPortraits setThoughtBasedExpressionPortraits
} from '../../core/state.js'; } from '../../core/state.js';
import { import {
getCurrentMessageSwipeTrackerData, getCurrentMessageSwipeTrackerData,
saveChatData, saveChatData,
setMessageSwipeTrackerField setMessageSwipeTrackerField
} from '../../core/persistence.js'; } from '../../core/persistence.js';
import { isUsableExpressionSrc } from '../../utils/expressionPortraits.js'; import { isUsableThoughtBasedExpressionSrc } from '../../utils/thoughtBasedExpressionPortraits.js';
import { import {
getPresentCharactersTrackerData, getPresentCharactersTrackerData,
parsePresentCharacters parsePresentCharacters
@@ -35,16 +35,16 @@ import {
const OFF_SCENE_THOUGHT_PATTERN = /\b(not\s+(currently\s+)?(in|at|present|in\s+the)\s+(the\s+)?(scene|area|room|location|vicinity))\b|\b(off[\s-]?scene)\b|\b(not\s+present)\b|\b(absent)\b|\b(away\s+from\s+(the\s+)?scene)\b/i; const OFF_SCENE_THOUGHT_PATTERN = /\b(not\s+(currently\s+)?(in|at|present|in\s+the)\s+(the\s+)?(scene|area|room|location|vicinity))\b|\b(off[\s-]?scene)\b|\b(not\s+present)\b|\b(absent)\b|\b(away\s+from\s+(the\s+)?scene)\b/i;
const CHAT_CHANGE_RETRY_DELAYS = [0, 80, 220, 500]; const CHAT_CHANGE_RETRY_DELAYS = [0, 80, 220, 500];
const SYNC_DEBOUNCE_DELAY = 80; const REFRESH_DEBOUNCE_DELAY = 80;
const EXPRESSION_SYNC_CACHE_VERSION = 1; const THOUGHT_BASED_EXPRESSIONS_CACHE_VERSION = 1;
const EXPRESSION_SYNC_CACHE_FIELD = 'expressionSync'; const THOUGHT_BASED_EXPRESSIONS_CACHE_FIELD = 'thoughtBasedExpressions';
let hiddenExpressionStyleElement = null; let hiddenExpressionStyleElement = null;
let refreshExpressionConsumersHandler = null; let thoughtBasedExpressionsRefreshHandler = null;
let scheduledSyncTimer = null; let scheduledRefreshTimer = null;
let activeSyncRunId = 0; let activeRefreshRunId = 0;
let lastCompletedSyncSignature = null; let lastCompletedRefreshSignature = null;
let lastExpressionsSettingsSignature = null; let lastExpressionSettingsSignature = null;
function normalizeName(name) { function normalizeName(name) {
return String(name || '').trim().toLowerCase(); return String(name || '').trim().toLowerCase();
@@ -54,14 +54,14 @@ function shouldHideNativeExpressionDisplay() {
return extensionSettings.enabled === true && extensionSettings.hideDefaultExpressionDisplay === true; return extensionSettings.enabled === true && extensionSettings.hideDefaultExpressionDisplay === true;
} }
function shouldSyncExpressionPortraits() { function shouldUseThoughtBasedExpressions() {
return extensionSettings.enabled === true return extensionSettings.enabled === true
&& extensionSettings.syncExpressionsToPresentCharacters === true && extensionSettings.enableThoughtBasedExpressions === true
&& extensionSettings.showAlternatePresentCharactersPanel === true; && extensionSettings.showAlternatePresentCharactersPanel === true;
} }
function refreshExpressionConsumers() { function notifyThoughtBasedExpressionsConsumers() {
refreshExpressionConsumersHandler?.(); thoughtBasedExpressionsRefreshHandler?.();
} }
function getHideStyleCss() { function getHideStyleCss() {
@@ -109,7 +109,7 @@ function showNativeExpressionDisplay() {
hiddenExpressionStyleElement = null; hiddenExpressionStyleElement = null;
} }
function syncNativeExpressionDisplayVisibility() { function updateNativeExpressionDisplayVisibility() {
if (shouldHideNativeExpressionDisplay()) { if (shouldHideNativeExpressionDisplay()) {
hideNativeExpressionDisplay(); hideNativeExpressionDisplay();
} else { } else {
@@ -117,10 +117,10 @@ function syncNativeExpressionDisplayVisibility() {
} }
} }
function clearScheduledSync() { function clearScheduledRefresh() {
if (scheduledSyncTimer !== null) { if (scheduledRefreshTimer !== null) {
clearTimeout(scheduledSyncTimer); clearTimeout(scheduledRefreshTimer);
scheduledSyncTimer = null; scheduledRefreshTimer = null;
} }
} }
@@ -176,25 +176,25 @@ function arePortraitMapsEqual(left, right) {
return leftKeys.every(key => left[key] === right[key]); return leftKeys.every(key => left[key] === right[key]);
} }
function applySyncedExpressionPortraits(nextPortraits) { function applyThoughtBasedExpressionPortraits(nextPortraits) {
if (arePortraitMapsEqual(syncedExpressionPortraits, nextPortraits)) { if (arePortraitMapsEqual(thoughtBasedExpressionPortraits, nextPortraits)) {
return false; return false;
} }
setSyncedExpressionPortraits(nextPortraits); setThoughtBasedExpressionPortraits(nextPortraits);
return true; return true;
} }
function purgeInvalidSyncedExpressionPortraits() { function purgeInvalidThoughtBasedExpressionPortraits() {
const nextPortraits = {}; const nextPortraits = {};
for (const [characterName, src] of Object.entries(syncedExpressionPortraits)) { for (const [characterName, src] of Object.entries(thoughtBasedExpressionPortraits)) {
if (isUsableExpressionSrc(src)) { if (isUsableThoughtBasedExpressionSrc(src)) {
nextPortraits[characterName] = src; nextPortraits[characterName] = src;
} }
} }
return applySyncedExpressionPortraits(nextPortraits); return applyThoughtBasedExpressionPortraits(nextPortraits);
} }
function getMessageThoughtPayload(message) { function getMessageThoughtPayload(message) {
@@ -242,24 +242,28 @@ function findThoughtSourceMessageInfo(characterThoughtsData) {
return currentThoughts ? null : fallback; return currentThoughts ? null : fallback;
} }
function getSwipeExpressionSyncCache(sourceInfo) { function isThoughtBasedExpressionsCache(candidate) {
const cache = sourceInfo?.swipeData?.[EXPRESSION_SYNC_CACHE_FIELD]; return !!(
if (!cache || typeof cache !== 'object' || Array.isArray(cache)) { candidate
return null; && typeof candidate === 'object'
&& !Array.isArray(candidate)
&& candidate.version === THOUGHT_BASED_EXPRESSIONS_CACHE_VERSION
&& candidate.entries
&& typeof candidate.entries === 'object'
&& !Array.isArray(candidate.entries)
);
} }
if (cache.version !== EXPRESSION_SYNC_CACHE_VERSION) { function getSwipeThoughtBasedExpressionsCache(sourceInfo) {
return null; const directCache = sourceInfo?.swipeData?.[THOUGHT_BASED_EXPRESSIONS_CACHE_FIELD];
return isThoughtBasedExpressionsCache(directCache) ? directCache : null;
} }
return cache; function areThoughtBasedExpressionsCachesEqual(left, right) {
}
function areExpressionSyncCachesEqual(left, right) {
return stableStringify(left) === stableStringify(right); return stableStringify(left) === stableStringify(right);
} }
function getThoughtSyncEntries(characterThoughtsData) { function getThoughtBasedExpressionEntries(characterThoughtsData) {
const thoughtsConfig = extensionSettings.trackerConfig?.presentCharacters?.thoughts; const thoughtsConfig = extensionSettings.trackerConfig?.presentCharacters?.thoughts;
if (thoughtsConfig?.enabled === false) { if (thoughtsConfig?.enabled === false) {
return []; return [];
@@ -278,7 +282,7 @@ function getThoughtSyncEntries(characterThoughtsData) {
.filter(character => character.name && character.thought && !OFF_SCENE_THOUGHT_PATTERN.test(character.thought)); .filter(character => character.name && character.thought && !OFF_SCENE_THOUGHT_PATTERN.test(character.thought));
} }
function buildSyncSignature(thoughtEntries, expressionsSettingsSignature) { function buildRefreshSignature(thoughtEntries, expressionsSettingsSignature) {
return JSON.stringify({ return JSON.stringify({
expressionsSettingsSignature, expressionsSettingsSignature,
thoughtEntries: thoughtEntries.map(entry => ({ thoughtEntries: thoughtEntries.map(entry => ({
@@ -289,53 +293,53 @@ function buildSyncSignature(thoughtEntries, expressionsSettingsSignature) {
}); });
} }
async function syncExpressionsFromThoughts({ force = false } = {}) { async function refreshThoughtBasedExpressions({ force = false } = {}) {
syncNativeExpressionDisplayVisibility(); updateNativeExpressionDisplayVisibility();
if (!extensionSettings.enabled) { if (!extensionSettings.enabled) {
showNativeExpressionDisplay(); showNativeExpressionDisplay();
return; return;
} }
if (!shouldSyncExpressionPortraits()) { if (!shouldUseThoughtBasedExpressions()) {
return; return;
} }
if (!isExpressionsExtensionEnabled()) { if (!isExpressionsExtensionEnabled()) {
lastCompletedSyncSignature = null; lastCompletedRefreshSignature = null;
lastExpressionsSettingsSignature = null; lastExpressionSettingsSignature = null;
clearExpressionsCompatibilityCache(); clearExpressionsCompatibilityCache();
const portraitsChanged = applySyncedExpressionPortraits({}); const portraitsChanged = applyThoughtBasedExpressionPortraits({});
if (portraitsChanged) { if (portraitsChanged) {
saveChatData(); saveChatData();
} }
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
return; return;
} }
const expressionsSettingsSignature = getExpressionsSettingsSignature(); const expressionsSettingsSignature = getExpressionsSettingsSignature();
if (expressionsSettingsSignature !== lastExpressionsSettingsSignature) { if (expressionsSettingsSignature !== lastExpressionSettingsSignature) {
clearExpressionsCompatibilityCache(); clearExpressionsCompatibilityCache();
lastExpressionsSettingsSignature = expressionsSettingsSignature; lastExpressionSettingsSignature = expressionsSettingsSignature;
lastCompletedSyncSignature = null; lastCompletedRefreshSignature = null;
} }
const characterThoughtsData = getPresentCharactersTrackerData({ useCommittedFallback: true }); const characterThoughtsData = getPresentCharactersTrackerData({ useCommittedFallback: true });
const thoughtEntries = getThoughtSyncEntries(characterThoughtsData); const thoughtEntries = getThoughtBasedExpressionEntries(characterThoughtsData);
const syncSignature = buildSyncSignature(thoughtEntries, expressionsSettingsSignature); const refreshSignature = buildRefreshSignature(thoughtEntries, expressionsSettingsSignature);
if (!force && syncSignature === lastCompletedSyncSignature) { if (!force && refreshSignature === lastCompletedRefreshSignature) {
return; return;
} }
const sourceInfo = findThoughtSourceMessageInfo(characterThoughtsData); const sourceInfo = findThoughtSourceMessageInfo(characterThoughtsData);
const cachedSyncData = getSwipeExpressionSyncCache(sourceInfo); const cachedThoughtBasedExpressions = getSwipeThoughtBasedExpressionsCache(sourceInfo);
const cachedEntries = cachedSyncData?.entries && typeof cachedSyncData.entries === 'object' && !Array.isArray(cachedSyncData.entries) const cachedEntries = cachedThoughtBasedExpressions?.entries && typeof cachedThoughtBasedExpressions.entries === 'object' && !Array.isArray(cachedThoughtBasedExpressions.entries)
? cachedSyncData.entries ? cachedThoughtBasedExpressions.entries
: {}; : {};
const currentThoughtsSignature = normalizeThoughtPayload(characterThoughtsData); const currentThoughtsSignature = normalizeThoughtPayload(characterThoughtsData);
const classificationSettingsSignature = getExpressionClassificationSettingsSignature(); const classificationSettingsSignature = getExpressionClassificationSettingsSignature();
const portraitSettingsSignature = getExpressionPortraitSettingsSignature(); const portraitSettingsSignature = getExpressionPortraitSettingsSignature();
const runId = ++activeSyncRunId; const runId = ++activeRefreshRunId;
const nextPortraits = {}; const nextPortraits = {};
const nextCacheEntries = {}; const nextCacheEntries = {};
@@ -349,7 +353,7 @@ async function syncExpressionsFromThoughts({ force = false } = {}) {
const cachedEntry = cachedEntries[portraitKey] && typeof cachedEntries[portraitKey] === 'object' const cachedEntry = cachedEntries[portraitKey] && typeof cachedEntries[portraitKey] === 'object'
? cachedEntries[portraitKey] ? cachedEntries[portraitKey]
: null; : null;
const previousSrc = nextPortraits[portraitKey] || syncedExpressionPortraits[portraitKey] || null; const previousSrc = nextPortraits[portraitKey] || thoughtBasedExpressionPortraits[portraitKey] || null;
const canReuseExpression = cachedEntry const canReuseExpression = cachedEntry
&& cachedEntry.thought === entry.thought && cachedEntry.thought === entry.thought
&& cachedEntry.classificationSettingsSignature === classificationSettingsSignature && cachedEntry.classificationSettingsSignature === classificationSettingsSignature
@@ -359,7 +363,7 @@ async function syncExpressionsFromThoughts({ force = false } = {}) {
const expression = canReuseExpression const expression = canReuseExpression
? normalizeExpressionLabel(cachedEntry.expression) ? normalizeExpressionLabel(cachedEntry.expression)
: normalizeExpressionLabel(await classifyExpressionText(entry.thought, { characterName: entry.name })); : normalizeExpressionLabel(await classifyExpressionText(entry.thought, { characterName: entry.name }));
if (runId !== activeSyncRunId) { if (runId !== activeRefreshRunId) {
return; return;
} }
@@ -371,13 +375,13 @@ async function syncExpressionsFromThoughts({ force = false } = {}) {
&& cachedEntry.portraitResolved === true; && cachedEntry.portraitResolved === true;
const portraitSrc = canReusePortrait const portraitSrc = canReusePortrait
? (isUsableExpressionSrc(cachedEntry.portraitSrc) ? cachedEntry.portraitSrc : null) ? (isUsableThoughtBasedExpressionSrc(cachedEntry.portraitSrc) ? cachedEntry.portraitSrc : null)
: await resolveExpressionPortraitForCharacter(entry.name, expression, { previousSrc }); : await resolveExpressionPortraitForCharacter(entry.name, expression, { previousSrc });
if (runId !== activeSyncRunId) { if (runId !== activeRefreshRunId) {
return; return;
} }
if (isUsableExpressionSrc(portraitSrc)) { if (isUsableThoughtBasedExpressionSrc(portraitSrc)) {
nextPortraits[portraitKey] = portraitSrc; nextPortraits[portraitKey] = portraitSrc;
} }
@@ -388,159 +392,159 @@ async function syncExpressionsFromThoughts({ force = false } = {}) {
classificationSettingsSignature, classificationSettingsSignature,
portraitSettingsSignature, portraitSettingsSignature,
expression, expression,
portraitSrc: isUsableExpressionSrc(portraitSrc) ? portraitSrc : null, portraitSrc: isUsableThoughtBasedExpressionSrc(portraitSrc) ? portraitSrc : null,
portraitResolved: true portraitResolved: true
}; };
} }
if (runId !== activeSyncRunId) { if (runId !== activeRefreshRunId) {
return; return;
} }
let cacheChanged = false; let cacheChanged = false;
if (sourceInfo) { if (sourceInfo) {
const nextCache = { const nextCache = {
version: EXPRESSION_SYNC_CACHE_VERSION, version: THOUGHT_BASED_EXPRESSIONS_CACHE_VERSION,
thoughtsSignature: currentThoughtsSignature, thoughtsSignature: currentThoughtsSignature,
entries: nextCacheEntries entries: nextCacheEntries
}; };
if (!areExpressionSyncCachesEqual(cachedSyncData, nextCache)) { if (!areThoughtBasedExpressionsCachesEqual(cachedThoughtBasedExpressions, nextCache)) {
setMessageSwipeTrackerField(sourceInfo.message, sourceInfo.swipeId, EXPRESSION_SYNC_CACHE_FIELD, nextCache); setMessageSwipeTrackerField(sourceInfo.message, sourceInfo.swipeId, THOUGHT_BASED_EXPRESSIONS_CACHE_FIELD, nextCache);
cacheChanged = true; cacheChanged = true;
} }
} }
lastCompletedSyncSignature = syncSignature; lastCompletedRefreshSignature = refreshSignature;
const portraitsChanged = applySyncedExpressionPortraits(nextPortraits); const portraitsChanged = applyThoughtBasedExpressionPortraits(nextPortraits);
if (portraitsChanged || cacheChanged) { if (portraitsChanged || cacheChanged) {
saveChatData(); saveChatData();
} }
if (portraitsChanged) { if (portraitsChanged) {
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
} }
} }
export function setExpressionSyncRefreshHandler(handler) { export function setThoughtBasedExpressionsRefreshHandler(handler) {
refreshExpressionConsumersHandler = typeof handler === 'function' ? handler : null; thoughtBasedExpressionsRefreshHandler = typeof handler === 'function' ? handler : null;
} }
export function queueExpressionSyncFromThoughts({ immediate = false, force = false } = {}) { export function queueThoughtBasedExpressionsUpdate({ immediate = false, force = false } = {}) {
clearScheduledSync(); clearScheduledRefresh();
const runSync = () => { const runRefresh = () => {
syncExpressionsFromThoughts({ force }).catch(error => { refreshThoughtBasedExpressions({ force }).catch(error => {
console.warn('[RPG Companion] Thoughts-driven expression sync failed:', error); console.warn('[RPG Companion] Thought-based expressions update failed:', error);
}); });
}; };
if (immediate) { if (immediate) {
runSync(); runRefresh();
return; return;
} }
scheduledSyncTimer = setTimeout(() => { scheduledRefreshTimer = setTimeout(() => {
scheduledSyncTimer = null; scheduledRefreshTimer = null;
runSync(); runRefresh();
}, SYNC_DEBOUNCE_DELAY); }, REFRESH_DEBOUNCE_DELAY);
} }
export function initExpressionSync() { export function initThoughtBasedExpressions() {
const purged = purgeInvalidSyncedExpressionPortraits(); const purged = purgeInvalidThoughtBasedExpressionPortraits();
syncNativeExpressionDisplayVisibility(); updateNativeExpressionDisplayVisibility();
if (purged) { if (purged) {
saveChatData(); saveChatData();
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
} }
if (shouldSyncExpressionPortraits()) { if (shouldUseThoughtBasedExpressions()) {
queueExpressionSyncFromThoughts({ immediate: true, force: true }); queueThoughtBasedExpressionsUpdate({ immediate: true, force: true });
} }
} }
export function onExpressionSyncChatChanged() { export function onThoughtBasedExpressionsChatChanged() {
if (!extensionSettings.enabled) { if (!extensionSettings.enabled) {
showNativeExpressionDisplay(); showNativeExpressionDisplay();
return; return;
} }
clearScheduledSync(); clearScheduledRefresh();
activeSyncRunId += 1; activeRefreshRunId += 1;
lastCompletedSyncSignature = null; lastCompletedRefreshSignature = null;
lastExpressionsSettingsSignature = null; lastExpressionSettingsSignature = null;
clearExpressionsCompatibilityCache(); clearExpressionsCompatibilityCache();
const purged = purgeInvalidSyncedExpressionPortraits(); const purged = purgeInvalidThoughtBasedExpressionPortraits();
if (purged) { if (purged) {
saveChatData(); saveChatData();
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
} }
for (const delay of CHAT_CHANGE_RETRY_DELAYS) { for (const delay of CHAT_CHANGE_RETRY_DELAYS) {
setTimeout(() => { setTimeout(() => {
syncNativeExpressionDisplayVisibility(); updateNativeExpressionDisplayVisibility();
if (shouldSyncExpressionPortraits()) { if (shouldUseThoughtBasedExpressions()) {
queueExpressionSyncFromThoughts({ immediate: true, force: true }); queueThoughtBasedExpressionsUpdate({ immediate: true, force: true });
} else { } else {
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
} }
}, delay); }, delay);
} }
} }
export function onExpressionSyncSettingChanged(enabled) { export function onThoughtBasedExpressionsSettingChanged(enabled) {
syncNativeExpressionDisplayVisibility(); updateNativeExpressionDisplayVisibility();
if (enabled) { if (enabled) {
const purged = purgeInvalidSyncedExpressionPortraits(); const purged = purgeInvalidThoughtBasedExpressionPortraits();
if (purged) { if (purged) {
saveChatData(); saveChatData();
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
} }
if (shouldSyncExpressionPortraits()) { if (shouldUseThoughtBasedExpressions()) {
queueExpressionSyncFromThoughts({ immediate: true, force: true }); queueThoughtBasedExpressionsUpdate({ immediate: true, force: true });
} else { } else {
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
} }
return; return;
} }
clearScheduledSync(); clearScheduledRefresh();
activeSyncRunId += 1; activeRefreshRunId += 1;
lastCompletedSyncSignature = null; lastCompletedRefreshSignature = null;
lastExpressionsSettingsSignature = null; lastExpressionSettingsSignature = null;
clearExpressionsCompatibilityCache(); clearExpressionsCompatibilityCache();
refreshExpressionConsumers(); notifyThoughtBasedExpressionsConsumers();
} }
export function onAlternatePresentCharactersVisibilityChanged() { export function onAlternatePresentCharactersVisibilityChanged() {
syncNativeExpressionDisplayVisibility(); updateNativeExpressionDisplayVisibility();
if (shouldSyncExpressionPortraits()) { if (shouldUseThoughtBasedExpressions()) {
queueExpressionSyncFromThoughts({ immediate: true, force: true }); queueThoughtBasedExpressionsUpdate({ immediate: true, force: true });
return; return;
} }
clearScheduledSync(); clearScheduledRefresh();
activeSyncRunId += 1; activeRefreshRunId += 1;
lastCompletedSyncSignature = null; lastCompletedRefreshSignature = null;
lastExpressionsSettingsSignature = null; lastExpressionSettingsSignature = null;
} }
export function onHideDefaultExpressionDisplaySettingChanged(enabled) { export function onHideDefaultExpressionDisplaySettingChanged(enabled) {
extensionSettings.hideDefaultExpressionDisplay = enabled === true; extensionSettings.hideDefaultExpressionDisplay = enabled === true;
syncNativeExpressionDisplayVisibility(); updateNativeExpressionDisplayVisibility();
setTimeout(() => syncNativeExpressionDisplayVisibility(), 0); setTimeout(() => updateNativeExpressionDisplayVisibility(), 0);
setTimeout(() => syncNativeExpressionDisplayVisibility(), 120); setTimeout(() => updateNativeExpressionDisplayVisibility(), 120);
} }
export function clearExpressionSyncCache() { export function clearThoughtBasedExpressionsCache() {
clearScheduledSync(); clearScheduledRefresh();
activeSyncRunId += 1; activeRefreshRunId += 1;
lastCompletedSyncSignature = null; lastCompletedRefreshSignature = null;
lastExpressionsSettingsSignature = null; lastExpressionSettingsSignature = null;
clearExpressionsCompatibilityCache(); clearExpressionsCompatibilityCache();
showNativeExpressionDisplay(); showNativeExpressionDisplay();
} }
+3 -3
View File
@@ -22,7 +22,7 @@ import {
} from '../../utils/presentCharacters.js'; } from '../../utils/presentCharacters.js';
import { isItemLocked, setItemLock } from '../generation/lockManager.js'; import { isItemLocked, setItemLock } from '../generation/lockManager.js';
import { renderAlternatePresentCharacters } from '../ui/alternatePresentCharacters.js'; import { renderAlternatePresentCharacters } from '../ui/alternatePresentCharacters.js';
import { queueExpressionSyncFromThoughts } from '../integration/expressionSync.js'; import { queueThoughtBasedExpressionsUpdate } from '../integration/thoughtBasedExpressions.js';
/** /**
* Helper to generate lock icon HTML if setting is enabled * Helper to generate lock icon HTML if setting is enabled
@@ -93,7 +93,7 @@ function getStatColor(percentage, lowColor, highColor, lowOpacity = 100, highOpa
*/ */
export function renderThoughts({ preserveScroll = false, useCommittedFallback = true } = {}) { export function renderThoughts({ preserveScroll = false, useCommittedFallback = true } = {}) {
renderAlternatePresentCharacters({ useCommittedFallback }); renderAlternatePresentCharacters({ useCommittedFallback });
queueExpressionSyncFromThoughts(); queueThoughtBasedExpressionsUpdate();
if (!extensionSettings.showCharacterThoughts || !$thoughtsContainer) { if (!extensionSettings.showCharacterThoughts || !$thoughtsContainer) {
return; return;
@@ -1284,7 +1284,7 @@ export function updateCharacterField(characterName, field, value) {
// console.log('[RPG Companion] After update - lastGeneratedData.characterThoughts:', lastGeneratedData.characterThoughts); // console.log('[RPG Companion] After update - lastGeneratedData.characterThoughts:', lastGeneratedData.characterThoughts);
if (field === 'name' || isEditingThoughts) { if (field === 'name' || isEditingThoughts) {
queueExpressionSyncFromThoughts({ immediate: true, force: true }); queueThoughtBasedExpressionsUpdate({ immediate: true, force: true });
} }
if (isEditingThoughts && extensionSettings.showThoughtsInChat) { if (isEditingThoughts && extensionSettings.showThoughtsInChat) {
+3 -3
View File
@@ -1,6 +1,6 @@
import { extensionSettings } from '../../core/state.js'; import { extensionSettings } from '../../core/state.js';
import { i18n } from '../../core/i18n.js'; import { i18n } from '../../core/i18n.js';
import { getExpressionPortraitForCharacter } from '../../utils/expressionPortraits.js'; import { getThoughtBasedExpressionPortraitForCharacter } from '../../utils/thoughtBasedExpressionPortraits.js';
import { getSafeImageSrc } from '../../utils/imageUrls.js'; import { getSafeImageSrc } from '../../utils/imageUrls.js';
import { import {
getPresentCharactersTrackerData, getPresentCharactersTrackerData,
@@ -49,8 +49,8 @@ function handlePortraitLoadError() {
} }
function createAlternatePresentCharacterCard(character) { function createAlternatePresentCharacterCard(character) {
const rawPortrait = (extensionSettings.syncExpressionsToPresentCharacters const rawPortrait = (extensionSettings.enableThoughtBasedExpressions
? getExpressionPortraitForCharacter(character.name) ? getThoughtBasedExpressionPortraitForCharacter(character.name)
: null) || resolvePresentCharacterPortrait(character.name); : null) || resolvePresentCharacterPortrait(character.name);
const portrait = getSafeImageSrc(rawPortrait); const portrait = getSafeImageSrc(rawPortrait);
const name = String(character.name || ''); const name = String(character.name || '');
+2 -2
View File
@@ -11,7 +11,7 @@ import {
$infoBoxContainer, $infoBoxContainer,
$thoughtsContainer, $thoughtsContainer,
$userStatsContainer, $userStatsContainer,
clearSyncedExpressionPortraits, clearThoughtBasedExpressionPortraits,
setPendingDiceRoll, setPendingDiceRoll,
getPendingDiceRoll, getPendingDiceRoll,
clearSessionAvatarPrompts clearSessionAvatarPrompts
@@ -371,7 +371,7 @@ export function setupSettingsPopup() {
// Clear session avatar prompts // Clear session avatar prompts
clearSessionAvatarPrompts(); clearSessionAvatarPrompts();
clearSyncedExpressionPortraits(); clearThoughtBasedExpressionPortraits();
// Clear chat metadata immediately (don't wait for debounced save) // Clear chat metadata immediately (don't wait for debounced save)
const context = getContext(); const context = getContext();
@@ -1,6 +1,6 @@
import { import {
syncedExpressionPortraits, thoughtBasedExpressionPortraits,
getSyncedExpressionPortrait getThoughtBasedExpressionPortrait
} from '../core/state.js'; } from '../core/state.js';
import { import {
isSafeImageSrc, isSafeImageSrc,
@@ -35,7 +35,7 @@ function isDocumentLikeUrl(src) {
&& candidate.search === current.search; && candidate.search === current.search;
} }
export function isUsableExpressionSrc(src) { export function isUsableThoughtBasedExpressionSrc(src) {
const normalized = normalizeImageSrc(src); const normalized = normalizeImageSrc(src);
if (!normalized) { if (!normalized) {
return false; return false;
@@ -48,7 +48,7 @@ export function isUsableExpressionSrc(src) {
return isSafeImageSrc(normalized); return isSafeImageSrc(normalized);
} }
export function getExpressionPortraitForCharacter(characterName) { export function getThoughtBasedExpressionPortraitForCharacter(characterName) {
if (!isExpressionsExtensionEnabled()) { if (!isExpressionsExtensionEnabled()) {
return null; return null;
} }
@@ -58,13 +58,13 @@ export function getExpressionPortraitForCharacter(characterName) {
return null; return null;
} }
const exact = getSyncedExpressionPortrait(target); const exact = getThoughtBasedExpressionPortrait(target);
if (isUsableExpressionSrc(exact)) { if (isUsableThoughtBasedExpressionSrc(exact)) {
return exact; return exact;
} }
for (const [storedName, src] of Object.entries(syncedExpressionPortraits)) { for (const [storedName, src] of Object.entries(thoughtBasedExpressionPortraits)) {
if (namesMatch(storedName, target) && isUsableExpressionSrc(src)) { if (namesMatch(storedName, target) && isUsableThoughtBasedExpressionSrc(src)) {
return src; return src;
} }
} }
+4 -4
View File
@@ -368,12 +368,12 @@
</small> </small>
<label class="checkbox_label"> <label class="checkbox_label">
<input type="checkbox" id="rpg-toggle-sync-expressions" /> <input type="checkbox" id="rpg-toggle-thought-based-expressions" />
<span data-i18n-key="template.settingsModal.display.syncBelowChatPresentCharactersExpressions">Sync Expressions in Below-Chat Panel</span> <span data-i18n-key="template.settingsModal.display.thoughtBasedExpressions">Thought-Based Expressions</span>
</label> </label>
<small style="display: block; margin-left: 24px; margin-top: -8px; color: #888; font-size: 11px;" <small style="display: block; margin-left: 24px; margin-top: -8px; color: #888; font-size: 11px;"
data-i18n-key="template.settingsModal.display.syncBelowChatPresentCharactersExpressionsNote"> data-i18n-key="template.settingsModal.display.thoughtBasedExpressionsNote">
Use each character's current SillyTavern expression portrait in the below-chat Present Characters panel. Use SillyTavern Character Expressions to classify each present character's thoughts for the below-chat panel. May increase token usage depending on the selected Classifier API.
</small> </small>
<label class="checkbox_label"> <label class="checkbox_label">