Fixes #11: Memory management enhancements

- Add clearDomCache() for jQuery object cache invalidation on panel rebuild
- Track all event handlers in on()/once() for complete cleanup by unregisterAllEvents()
- Migrate direct eventSource.on() calls to tracked onEvent() in index.js, injector.js, sillyTavernExpressions.js
- Add clearDebugLogs() called on CHAT_CHANGED to prevent memory accumulation
- Add trackJQueryHandler()/cleanupJQueryEvents() infrastructure for jQuery event cleanup
- Add comprehensive memory management tests (14 new tests, all passing)
This commit is contained in:
2026-07-12 14:31:14 +02:00
parent 3c4e632906
commit 9c0f09619d
6 changed files with 332 additions and 15 deletions
+15 -9
View File
@@ -50,10 +50,12 @@ import {
setEquipmentContainer,
setQuestsContainer,
setMusicPlayerContainer,
clearSessionAvatarPrompts
clearSessionAvatarPrompts,
clearDomCache,
clearDebugLogs
} from './src/core/state.js';
import { loadSettings, saveSettings, saveChatData, loadChatData, updateMessageSwipeData, commitTrackerDataFromPriorMessage } from './src/core/persistence.js';
import { registerAllEvents } from './src/core/events.js';
import { registerAllEvents, on as onEvent } from './src/core/events.js';
import { addExtensionSettings } from './src/core/settingsPanel.js';
// Generation & Parsing modules
@@ -233,6 +235,8 @@ async function initUI() {
}
// Cache UI elements using state setters
// Clear stale DOM references first to prevent memory leaks on panel rebuild
clearDomCache();
setPanelContainer($('#rpg-companion-panel'));
setUserStatsContainer($('#rpg-user-stats'));
setInfoBoxContainer($('#rpg-info-box'));
@@ -372,7 +376,7 @@ jQuery(async () => {
[event_types.MESSAGE_RECEIVED]: onMessageReceived,
[event_types.GENERATION_STOPPED]: onGenerationEnded,
[event_types.GENERATION_ENDED]: onGenerationEnded,
[event_types.CHAT_CHANGED]: [onCharacterChanged, updatePersonaAvatar, restoreCheckpointOnLoad, clearSessionAvatarPrompts],
[event_types.CHAT_CHANGED]: [onCharacterChanged, updatePersonaAvatar, restoreCheckpointOnLoad, clearSessionAvatarPrompts, clearDebugLogs],
[event_types.CHAT_LOADED]: onChatLoaded,
[event_types.MESSAGE_DELETED]: onMessageDeleted,
[event_types.MESSAGE_SWIPE_DELETED]: onMessageDeleted,
@@ -381,7 +385,9 @@ jQuery(async () => {
[event_types.SETTINGS_UPDATED]: updatePersonaAvatar
});
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, (messageId) => {
// Use tracked event registration (onEvent) instead of direct eventSource.on()
// This ensures all handlers are tracked for cleanup by unregisterAllEvents()
onEvent(event_types.CHARACTER_MESSAGE_RENDERED, (messageId) => {
if (!extensionSettings.enabled) return;
const renderedMessage = chat[messageId];
if (renderedMessage && !renderedMessage.is_user && !renderedMessage.is_system) {
@@ -389,7 +395,7 @@ jQuery(async () => {
}
});
eventSource.on(event_types.MESSAGE_UPDATED, (messageId) => {
onEvent(event_types.MESSAGE_UPDATED, (messageId) => {
if (!extensionSettings.enabled) return;
const updatedMessage = chat[messageId];
if (updatedMessage && !updatedMessage.is_user && !updatedMessage.is_system) {
@@ -397,7 +403,7 @@ jQuery(async () => {
}
});
eventSource.on(event_types.MESSAGE_SWIPED, (messageIndex) => {
onEvent(event_types.MESSAGE_SWIPED, (messageIndex) => {
if (!extensionSettings.enabled) return;
const swipedMessage = chat[messageIndex];
if (swipedMessage && !swipedMessage.is_user && !swipedMessage.is_system) {
@@ -405,18 +411,18 @@ jQuery(async () => {
}
});
eventSource.on(event_types.CHAT_CHANGED, () => {
onEvent(event_types.CHAT_CHANGED, () => {
clearThoughtBasedExpressionsCache();
setTimeout(() => onThoughtBasedExpressionsChatChanged(), 0);
});
eventSource.on(event_types.MESSAGE_DELETED, () => {
onEvent(event_types.MESSAGE_DELETED, () => {
if (!extensionSettings.enabled) return;
clearThoughtBasedExpressionsCache();
setTimeout(() => onThoughtBasedExpressionsChatChanged(), 0);
});
eventSource.on(event_types.MESSAGE_SWIPE_DELETED, () => {
onEvent(event_types.MESSAGE_SWIPE_DELETED, () => {
if (!extensionSettings.enabled) return;
clearThoughtBasedExpressionsCache();
setTimeout(() => onThoughtBasedExpressionsChatChanged(), 0);