Merge upstream/main into doom-lite-expression-sync-v2

This commit is contained in:
Tremendoussly
2026-04-14 16:21:07 +02:00
38 changed files with 1922 additions and 477 deletions
+138 -1
View File
@@ -480,6 +480,26 @@ export function saveChatData() {
saveChatDebounced();
}
/**
* Mirrors a tracker data entry into message.swipe_info so it survives page reloads.
* ST only serializes swipe_info to disk; message.extra is in-memory only.
* Guard: skips silently if swipe_info[swipeId] doesn't exist yet
*
* @param {Object} message - The chat message object
* @param {number} swipeId - The swipe index to mirror into
* @param {Object} swipeEntry - { userStats, infoBox, characterThoughts }
*/
export function mirrorToSwipeInfo(message, swipeId, swipeEntry) {
if (!message.swipe_info || !message.swipe_info[swipeId]) return;
if (!message.swipe_info[swipeId].extra) {
message.swipe_info[swipeId].extra = {};
}
if (!message.swipe_info[swipeId].extra.rpg_companion_swipes) {
message.swipe_info[swipeId].extra.rpg_companion_swipes = {};
}
message.swipe_info[swipeId].extra.rpg_companion_swipes[swipeId] = swipeEntry;
}
/**
* Updates the last assistant message's swipe data with current tracker data.
* This ensures user edits are preserved across swipes and included in generation context.
@@ -493,7 +513,7 @@ export function updateMessageSwipeData() {
// Find the last assistant message
for (let i = chat.length - 1; i >= 0; i--) {
const message = chat[i];
if (!message.is_user) {
if (!message.is_user && !message.is_system) {
// Found last assistant message - update its swipe data
if (!message.extra) {
message.extra = {};
@@ -515,6 +535,123 @@ export function updateMessageSwipeData() {
}
}
/**
* Reads RPG tracker data for a specific swipe from a message.
* Checks message.extra first (in-memory, current session), then message.swipe_info
* (serialized by SillyTavern on save, available after page reload).
*
* @param {Object} message - The chat message object
* @param {number} swipeId - The swipe index to read
* @returns {{userStats, infoBox, characterThoughts}|null} The swipe data or null
*/
export function getSwipeData(message, swipeId) {
// Primary: in-memory extra (current session or after a recent write)
const fromExtra = message.extra?.rpg_companion_swipes?.[swipeId];
if (fromExtra) return fromExtra;
// Fallback: swipe_info (populated by ST when loading from disk)
const fromSwipeInfo = message.swipe_info?.[swipeId]?.extra?.rpg_companion_swipes?.[swipeId];
if (fromSwipeInfo) return fromSwipeInfo;
return null;
}
/**
* Commits tracker data from the assistant message immediately before currentMessageIndex.
* Walks backward through the chat skipping the current message, user messages, and system
* messages until it finds the prior assistant message, then loads its active swipe data.
* If no prior assistant message exists or exists without a tracker state, nulls out all fields so
* the AI generates from an empty context rather than a ghost state.
*
* @param {number} currentMessageIndex - Index of the message to start searching before
*/
export function commitTrackerDataFromPriorMessage(currentMessageIndex) {
const chat = getContext().chat;
if (!chat || chat.length === 0) {
committedTrackerData.userStats = null;
committedTrackerData.infoBox = null;
committedTrackerData.characterThoughts = null;
return;
}
// console.log('[RPG Companion] commitTrackerDataFromPriorMessage called with index', currentMessageIndex, '| chat.length =', chat.length);
for (let i = currentMessageIndex - 1; i >= 0; i--) {
const message = chat[i];
if (message.is_user || message.is_system) continue;
// Found the prior assistant message — commit its active swipe data
const swipeId = message.swipe_id || 0;
const swipeData = getSwipeData(message, swipeId);
// console.log('[RPG Companion] Committing from chat[' + i + '] swipe', swipeId, '| has swipe data:', !!swipeData);
committedTrackerData.userStats = swipeData?.userStats || null;
committedTrackerData.infoBox = swipeData?.infoBox || null;
const rawCharacterThoughts = swipeData?.characterThoughts;
committedTrackerData.characterThoughts =
rawCharacterThoughts == null
? null
: (typeof rawCharacterThoughts === 'string'
? rawCharacterThoughts
: JSON.stringify(rawCharacterThoughts));
return;
}
// No prior assistant message found — use empty context
committedTrackerData.userStats = null;
committedTrackerData.infoBox = null;
committedTrackerData.characterThoughts = null;
}
/**
* Populates a message's current swipe slot with tracker data inherited from the
* nearest prior assistant message, when no tracker data has been generated for
* this swipe yet (e.g. auto-update is disabled).
*
* This ensures that commitTrackerDataFromPriorMessage can always find a tracker
* state to commit when the user sends the next message, rather than nulling
* everything out and resetting the tracker display to empty.
*
* Does nothing if the current swipe already has its own tracker data.
*
* @param {Object} message - The assistant message object to inherit into
* @param {number} messageIndex - Index of that message in chat
* @returns {boolean} True if inheritance was written, false otherwise
*/
export function inheritSwipeDataFromPriorMessage(message, messageIndex) {
const chat = getContext().chat;
if (!chat) return false;
const currentSwipeId = message.swipe_id || 0;
// Don't overwrite if this swipe already has its own tracker data.
if (getSwipeData(message, currentSwipeId)) return false;
// Walk backward to find the nearest prior assistant message with swipe data.
for (let i = messageIndex - 1; i >= 0; i--) {
const msg = chat[i];
if (msg.is_user || msg.is_system) continue;
const swipeId = msg.swipe_id || 0;
const swipeData = getSwipeData(msg, swipeId);
if (!swipeData) continue; // No data on this assistant message; keep searching further back
// Write inherited data into this swipe slot.
if (!message.extra) message.extra = {};
if (!message.extra.rpg_companion_swipes) message.extra.rpg_companion_swipes = {};
const inherited = {
userStats: swipeData.userStats,
infoBox: swipeData.infoBox,
characterThoughts: swipeData.characterThoughts
};
message.extra.rpg_companion_swipes[currentSwipeId] = inherited;
mirrorToSwipeInfo(message, currentSwipeId, inherited);
// console.log('[RPG Companion] Inherited tracker data from chat[' + i + '] into current swipe slot', currentSwipeId);
return true;
}
return false;
}
/**
* Loads RPG data from the current chat's metadata.
* Automatically migrates v1 inventory to v2 format if needed.