Release v3.0.0 - Major update with JSON format, lock/unlock trackers, reorganized UI, colored dialogues, editable prompts, and numerous bug fixes

This commit is contained in:
Spicy_Marinara
2026-01-07 17:22:22 +01:00
parent 8df6548e0b
commit c3cdac24c6
46 changed files with 6241 additions and 3571 deletions
+123 -74
View File
@@ -11,7 +11,8 @@ import {
lastGeneratedData,
isGenerating,
lastActionWasSwipe,
setLastActionWasSwipe
setLastActionWasSwipe,
setIsGenerating
} from '../../core/state.js';
import { evaluateSuppression } from './suppression.js';
import { parseUserStats } from './parser.js';
@@ -20,24 +21,38 @@ import {
generateTrackerInstructions,
generateContextualSummary,
DEFAULT_HTML_PROMPT,
DEFAULT_DIALOGUE_COLORING_PROMPT,
DEFAULT_SPOTIFY_PROMPT,
SPOTIFY_FORMAT_INSTRUCTION
} from './promptBuilder.js';
import { restoreCheckpointOnLoad } from '../features/chapterCheckpoint.js';
// Type imports
/** @typedef {import('../../types/inventory.js').InventoryV2} InventoryV2 */
// Track last chat length we committed at to prevent duplicate commits from streaming
let lastCommittedChatLength = -1;
/**
* Event handler for generation start.
* Manages tracker data commitment and prompt injection based on generation mode.
*
* @param {string} type - Event type
* @param {Object} data - Event data
* @param {boolean} dryRun - If true, this is a dry run (page reload, prompt preview, etc.) - skip all logic
*/
export async function onGenerationStarted(type, data) {
// console.log('[RPG Companion] onGenerationStarted called');
// console.log('[RPG Companion] enabled:', extensionSettings.enabled);
// console.log('[RPG Companion] generationMode:', extensionSettings.generationMode);
// console.log('[RPG Companion] ⚡ EVENT: onGenerationStarted - lastActionWasSwipe =', lastActionWasSwipe, '| isGenerating =', isGenerating);
// console.log('[RPG Companion] Committed Prompt:', committedTrackerData);
export async function onGenerationStarted(type, data, dryRun) {
// Skip dry runs (page reload, prompt manager preview, etc.)
if (dryRun) {
console.log('[RPG Companion] Skipping onGenerationStarted: dry run detected');
return;
}
console.log('[RPG Companion] onGenerationStarted called');
console.log('[RPG Companion] enabled:', extensionSettings.enabled);
console.log('[RPG Companion] generationMode:', extensionSettings.generationMode);
console.log('[RPG Companion] ⚡ EVENT: onGenerationStarted - lastActionWasSwipe =', lastActionWasSwipe, '| isGenerating =', isGenerating);
console.log('[RPG Companion] Committed Prompt:', committedTrackerData);
// Skip tracker injection for image generation requests
if (data?.quietImage) {
@@ -46,6 +61,13 @@ export async function onGenerationStarted(type, data) {
}
if (!extensionSettings.enabled) {
// Extension is disabled - clear any existing prompts to ensure nothing is injected
setExtensionPrompt('rpg-companion-inject', '', extension_prompt_types.IN_CHAT, 0, false);
setExtensionPrompt('rpg-companion-example', '', extension_prompt_types.IN_CHAT, 0, false);
setExtensionPrompt('rpg-companion-html', '', extension_prompt_types.IN_CHAT, 0, false);
setExtensionPrompt('rpg-companion-dialogue-coloring', '', extension_prompt_types.IN_CHAT, 0, false);
setExtensionPrompt('rpg-companion-spotify', '', extension_prompt_types.IN_CHAT, 0, false);
setExtensionPrompt('rpg-companion-context', '', extension_prompt_types.IN_CHAT, 1, false);
return;
}
@@ -76,8 +98,63 @@ export async function onGenerationStarted(type, data) {
// Ensure checkpoint is applied before generation
await restoreCheckpointOnLoad();
const currentChatLength = chat ? chat.length : 0;
const lastMessage = chat && chat.length > 0 ? chat[chat.length - 1] : null;
// For TOGETHER mode: Commit when user sends message (before first generation)
if (extensionSettings.generationMode === 'together') {
// By the time onGenerationStarted fires, ST has already added the placeholder AI message
// So we check the second-to-last message to see if user just sent a message
const secondToLastMessage = chat && chat.length > 1 ? chat[chat.length - 2] : null;
const isUserMessage = secondToLastMessage && secondToLastMessage.is_user;
// Commit if:
// 1. Second-to-last message is from USER (user just sent message)
// 2. Not a swipe (lastActionWasSwipe = false)
// 3. Haven't already committed for this chat length (prevent streaming duplicates)
const shouldCommit = isUserMessage && !lastActionWasSwipe && currentChatLength !== lastCommittedChatLength;
if (shouldCommit) {
console.log('[RPG Companion] 📝 TOGETHER MODE COMMIT: User sent message - committing data from BEFORE user message');
console.log('[RPG Companion] Chat length:', currentChatLength, 'Last committed:', lastCommittedChatLength);
console.log('[RPG Companion] BEFORE: committedTrackerData =', {
userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null'
});
console.log('[RPG Companion] BEFORE: lastGeneratedData =', {
userStats: lastGeneratedData.userStats ? `${lastGeneratedData.userStats.substring(0, 50)}...` : 'null',
infoBox: lastGeneratedData.infoBox ? 'exists' : 'null',
characterThoughts: lastGeneratedData.characterThoughts ? `${lastGeneratedData.characterThoughts.substring(0, 100)}...` : 'null'
});
// Commit displayed data (from before user sent message)
committedTrackerData.userStats = lastGeneratedData.userStats;
committedTrackerData.infoBox = lastGeneratedData.infoBox;
committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts;
// Track chat length to prevent duplicate commits
lastCommittedChatLength = currentChatLength;
console.log('[RPG Companion] AFTER: committedTrackerData =', {
userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null'
});
} else if (lastActionWasSwipe) {
console.log('[RPG Companion] ⏭️ Skipping commit: swipe (using previous committed data)');
} else if (!isUserMessage) {
console.log('[RPG Companion] ⏭️ Skipping commit: second-to-last message is not user message (likely swipe or continuation)');
}
console.log('[RPG Companion] 📦 TOGETHER MODE: Injecting committed tracker data into prompt');
console.log('[RPG Companion] committedTrackerData =', {
userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null'
});
}
// For SEPARATE mode only: Check if we need to commit extension data
// BUT: Only do this for the MAIN generation, not the tracker update generation
// If isGenerating is true, this is the tracker update generation (second call), so skip flag logic
@@ -85,83 +162,39 @@ export async function onGenerationStarted(type, data) {
if (extensionSettings.generationMode === 'separate' && !isGenerating) {
if (!lastActionWasSwipe) {
// User sent a new message - commit lastGeneratedData before generation
// console.log('[RPG Companion] 📝 COMMIT: New message - committing lastGeneratedData');
// console.log('[RPG Companion] BEFORE commit - committedTrackerData:', {
// userStats: committedTrackerData.userStats ? 'exists' : 'null',
// infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
// characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
// });
// console.log('[RPG Companion] BEFORE commit - lastGeneratedData:', {
// userStats: lastGeneratedData.userStats ? 'exists' : 'null',
// infoBox: lastGeneratedData.infoBox ? 'exists' : 'null',
// characterThoughts: lastGeneratedData.characterThoughts ? 'exists' : 'null'
// });
console.log('[RPG Companion] 📝 COMMIT: New message - committing lastGeneratedData');
console.log('[RPG Companion] BEFORE commit - committedTrackerData:', {
userStats: committedTrackerData.userStats ? 'exists' : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
});
console.log('[RPG Companion] BEFORE commit - lastGeneratedData:', {
userStats: lastGeneratedData.userStats ? 'exists' : 'null',
infoBox: lastGeneratedData.infoBox ? 'exists' : 'null',
characterThoughts: lastGeneratedData.characterThoughts ? 'exists' : 'null'
});
committedTrackerData.userStats = lastGeneratedData.userStats;
committedTrackerData.infoBox = lastGeneratedData.infoBox;
committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts;
// console.log('[RPG Companion] AFTER commit - committedTrackerData:', {
// userStats: committedTrackerData.userStats ? 'exists' : 'null',
// infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
// characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
// });
console.log('[RPG Companion] AFTER commit - committedTrackerData:', {
userStats: committedTrackerData.userStats ? 'exists' : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
});
// Reset flag after committing (ready for next cycle)
} else {
// console.log('[RPG Companion] 🔄 SWIPE: Using existing committedTrackerData (no commit)');
// console.log('[RPG Companion] committedTrackerData:', {
// userStats: committedTrackerData.userStats ? 'exists' : 'null',
// infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
// characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
// });
console.log('[RPG Companion] 🔄 SWIPE: Using existing committedTrackerData (no commit)');
console.log('[RPG Companion] committedTrackerData:', {
userStats: committedTrackerData.userStats ? 'exists' : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
});
// Reset flag after using it (swipe generation complete, ready for next action)
}
}
// For TOGETHER mode: Check if we need to commit extension data
// Only commit when user sends a new message (not on swipes)
if (extensionSettings.generationMode === 'together') {
if (!lastActionWasSwipe) {
// User sent a new message - commit data from the last assistant message they replied to
// This ensures swipes use consistent data from before the first swipe
console.log('[RPG Companion] 📝 TOGETHER MODE COMMIT: New message - committing from last assistant message');
// Find the last assistant message (before the user's new message)
const chat = getContext().chat;
let foundAssistantMessage = false;
for (let i = chat.length - 1; i >= 0; i--) {
const message = chat[i];
if (!message.is_user) {
// Found last assistant message - commit its stored tracker data
if (message.extra && message.extra.rpg_companion_swipes) {
const swipeId = message.swipe_id || 0;
const swipeData = message.extra.rpg_companion_swipes[swipeId];
if (swipeData) {
committedTrackerData.userStats = swipeData.userStats || null;
committedTrackerData.infoBox = swipeData.infoBox || null;
committedTrackerData.characterThoughts = swipeData.characterThoughts || null;
foundAssistantMessage = true;
console.log('[RPG Companion] ✓ Committed tracker data from message swipe', swipeId);
}
}
break;
}
}
// Fallback: if no stored data found, use lastGeneratedData (for first message)
if (!foundAssistantMessage) {
committedTrackerData.userStats = lastGeneratedData.userStats;
committedTrackerData.infoBox = lastGeneratedData.infoBox;
committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts;
console.log('[RPG Companion] ⚠ No stored message data found, using lastGeneratedData as fallback');
}
} else {
console.log('[RPG Companion] 🔄 TOGETHER MODE SWIPE: Using existing committedTrackerData (no commit)');
}
}
// Use the committed tracker data as source for generation
// console.log('[RPG Companion] Using committedTrackerData for generation');
// console.log('[RPG Companion] committedTrackerData.userStats:', committedTrackerData.userStats);
@@ -175,7 +208,10 @@ export async function onGenerationStarted(type, data) {
if (extensionSettings.generationMode === 'together') {
// console.log('[RPG Companion] In together mode, generating prompts...');
const example = generateTrackerExample();
const exampleRaw = generateTrackerExample();
// Wrap example in ```json``` code blocks for consistency with format instructions
// Add only 1 newline after the closing ``` (ST adds its own newline when injecting)
const example = exampleRaw ? `\`\`\`json\n${exampleRaw}\n\`\`\`\n` : null;
// Don't include HTML prompt in instructions - inject it separately to avoid duplication on swipes
const instructions = generateTrackerInstructions(false, true);
@@ -234,6 +270,19 @@ export async function onGenerationStarted(type, data) {
setExtensionPrompt('rpg-companion-html', '', extension_prompt_types.IN_CHAT, 0, false);
}
// Inject Dialogue Coloring prompt separately at depth 0 if enabled
if (extensionSettings.enableDialogueColoring && !shouldSuppress) {
// Use custom Dialogue Coloring prompt if set, otherwise use default
const dialogueColoringPromptText = extensionSettings.customDialogueColoringPrompt || DEFAULT_DIALOGUE_COLORING_PROMPT;
const dialogueColoringPrompt = `\n${dialogueColoringPromptText}`;
setExtensionPrompt('rpg-companion-dialogue-coloring', dialogueColoringPrompt, extension_prompt_types.IN_CHAT, 0, false);
// console.log('[RPG Companion] Injected Dialogue Coloring prompt at depth 0 for together mode');
} else {
// Clear Dialogue Coloring prompt if disabled
setExtensionPrompt('rpg-companion-dialogue-coloring', '', extension_prompt_types.IN_CHAT, 0, false);
}
// Inject Spotify prompt separately at depth 0 if enabled
if (extensionSettings.enableSpotifyMusic && !shouldSuppress) {
// Use custom Spotify prompt if set, otherwise use default