diff --git a/src/systems/features/avatarGenerator.js b/src/systems/features/avatarGenerator.js index 2673bf6..fc357de 100644 --- a/src/systems/features/avatarGenerator.js +++ b/src/systems/features/avatarGenerator.js @@ -10,6 +10,12 @@ import { saveSettings } from '../../core/persistence.js'; // Track pending avatar generations to avoid duplicate requests const pendingGenerations = new Set(); +/** + * Callback for when all avatar generations complete + * Used to trigger UI updates + */ +let onGenerationCompleteCallback = null; + /** * Style presets for avatar generation prompts */ @@ -32,6 +38,24 @@ function buildGenerationPrompt(characterName) { return `${style}, ${characterName}, ${custom}`.trim(); } +/** + * Sets a callback to be called when all avatar generations complete + * @param {Function} callback - Function to call when all generations are done + */ +export function setOnGenerationComplete(callback) { + onGenerationCompleteCallback = callback; +} + +/** + * Triggers the completion callback if all generations are done + */ +function checkAndTriggerCompletionCallback() { + if (pendingGenerations.size === 0 && onGenerationCompleteCallback) { + onGenerationCompleteCallback(); + onGenerationCompleteCallback = null; + } +} + /** * Generates an avatar for a character using /sd command * @param {string} characterName - Name of the character to generate avatar for @@ -61,10 +85,11 @@ export async function generateAvatar(characterName) { const prompt = buildGenerationPrompt(characterName); // Execute /sd command with quiet=true - // This saves to gallery without posting to chat + // IMPORTANT: quiet=true must come BEFORE the prompt + // This suppresses chat output and returns the image URL via pipe const result = await executeSlashCommandsOnChatInput( - `/sd ${prompt} quiet=true`, - { clearChatInput: false } + `/sd quiet=true ${prompt}`, + { clearChatInput: true } ); // The result might be an object with various properties @@ -102,6 +127,8 @@ export async function generateAvatar(characterName) { return null; } finally { pendingGenerations.delete(characterName); + // Check if all generations are complete and trigger callback + checkAndTriggerCompletionCallback(); } } @@ -134,3 +161,30 @@ export function checkAndGenerateAvatar(characterName, hasAvatar) { export function isGenerating(characterName) { return pendingGenerations.has(characterName); } + +/** + * Checks if ANY avatars are currently being generated + * @returns {boolean} True if any generation is in progress + */ +export function isAnyGenerating() { + return pendingGenerations.size > 0; +} + +/** + * Waits for all pending avatar generations to complete + * @returns {Promise} + */ +export function waitForAllGenerations() { + if (pendingGenerations.size === 0) { + return Promise.resolve(); + } + + return new Promise((resolve) => { + const checkInterval = setInterval(() => { + if (pendingGenerations.size === 0) { + clearInterval(checkInterval); + resolve(); + } + }, 100); + }); +} diff --git a/src/systems/generation/apiClient.js b/src/systems/generation/apiClient.js index c3e2c89..e1792f7 100644 --- a/src/systems/generation/apiClient.js +++ b/src/systems/generation/apiClient.js @@ -23,6 +23,7 @@ import { renderThoughts } from '../rendering/thoughts.js'; import { renderInventory } from '../rendering/inventory.js'; import { renderQuests } from '../rendering/quests.js'; import { i18n } from '../../core/i18n.js'; +import { setOnGenerationComplete, waitForAllGenerations } from '../features/avatarGenerator.js'; // Store the original preset name to restore after tracker generation let originalPresetName = null; @@ -214,6 +215,12 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough renderInventory(); renderQuests(); + // Set up callback to re-render thoughts when avatars finish generating + setOnGenerationComplete(() => { + console.log('[RPG Companion] Avatar generation complete, re-rendering thoughts...'); + renderThoughts(); + }); + // Save to chat metadata saveChatData(); } @@ -221,6 +228,11 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough } catch (error) { console.error('[RPG Companion] Error updating RPG data:', error); } finally { + // Wait for all avatar generations to complete before finishing + console.log('[RPG Companion] Waiting for avatar generations to complete...'); + await waitForAllGenerations(); + console.log('[RPG Companion] All avatar generations complete.'); + // Restore original preset if we switched to a separate one if (originalPresetName && extensionSettings.useSeparatePreset) { console.log(`[RPG Companion] Restoring original preset: "${originalPresetName}"`);