// SillyBubble - Dynamic Chat Bubble Image Generation Extension import { extension_settings, getContext } from "../../../extensions.js"; import { saveSettingsDebounced, callPopup } from "../../../../script.js"; // Extension configuration const extensionName = "SillyBubble"; const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`; // Default settings const defaultSettings = { image_service_url: "image.php", default_style: "default", enabled: true }; // Make sure settings exist if (!extension_settings[extensionName]) { extension_settings[extensionName] = {}; } // Apply defaults for any missing settings if (Object.keys(extension_settings[extensionName]).length === 0) { Object.assign(extension_settings[extensionName], defaultSettings); } // Function for AI to call - generates markdown image with URL-encoded text function generateChatBubbleImage(text, style) { if (!extension_settings[extensionName].enabled) { return text; } // Use default style if not specified const bubbleStyle = style || extension_settings[extensionName].default_style; // URL encode the text parameter const encodedText = encodeURIComponent(text); // Construct the URL with the encoded text const imageUrl = `${extension_settings[extensionName].image_service_url}?q=${encodedText}`; // Add style parameter if specified const fullUrl = bubbleStyle !== "default" ? `${imageUrl}&style=${bubbleStyle}` : imageUrl; // Return markdown image format return `![](${fullUrl})`; } // Load extension settings into UI function loadSettings() { // Update UI with current settings $("#sillybubble_enabled").prop("checked", extension_settings[extensionName].enabled).trigger("input"); $("#sillybubble_image_url").val(extension_settings[extensionName].image_service_url).trigger("input"); $("#sillybubble_default_style").val(extension_settings[extensionName].default_style).trigger("input"); } // Handle enable/disable toggle function onEnabledInput(event) { const value = Boolean($(event.target).prop("checked")); extension_settings[extensionName].enabled = value; saveSettingsDebounced(); } // Handle image service URL changes function onImageUrlInput(event) { const value = $(event.target).val(); extension_settings[extensionName].image_service_url = value; saveSettingsDebounced(); } // Handle default style changes function onDefaultStyleInput(event) { const value = $(event.target).val(); extension_settings[extensionName].default_style = value; saveSettingsDebounced(); } // Test function to visualize a bubble function onTestButtonClick() { const testText = "This is a test chat bubble generated by SillyBubble!"; const markdown = generateChatBubbleImage(testText); const testPopup = `

Generated Markdown:

${markdown.replace(//g, '>')}

Preview (if connected to image service):

${markdown}
`; callPopup(testPopup, 'text'); } // Register function tool function registerFunctionTool() { try { // Check if ToolManager exists in global scope if (!window.ToolManager) { console.warn(`[${extensionName}] ToolManager not found. Will try alternative registration methods.`); // Method 1: Register directly to window window.generateChatBubbleImage = generateChatBubbleImage; console.log(`[${extensionName}] Registered function to window object`); // Method 2: Add to extensions_functions if it exists if (window.extensions_functions) { window.extensions_functions['generateChatBubbleImage'] = generateChatBubbleImage; console.log(`[${extensionName}] Added to extensions_functions`); } return; } // Check if tool calling is supported if (typeof window.ToolManager.isToolCallingSupported !== 'function' || !window.ToolManager.isToolCallingSupported()) { console.warn(`[${extensionName}] Tool calling is not supported in the current configuration`); return; } // Define the function parameters schema const parameters = { "type": "object", "properties": { "text": { "type": "string", "description": "The text to display in the chat bubble" }, "style": { "type": "string", "description": "The visual style of the chat bubble (default, modern, retro, minimal)" } }, "required": ["text"] }; // Register the tool window.ToolManager.registerFunctionTool({ name: 'generateChatBubbleImage', displayName: 'Generate Chat Bubble', description: 'Creates a markdown image link with the text displayed as a chat bubble', parameters: parameters, action: async (params) => { return generateChatBubbleImage(params.text, params.style); }, formatMessage: async (params) => { return `Generating chat bubble with text: "${params.text?.substring(0, 30)}${params.text?.length > 30 ? '...' : ''}"`; } }); console.log(`[${extensionName}] Function tool registered via ToolManager`); } catch (error) { console.error(`[${extensionName}] Error registering function tool:`, error); } } // Initialize extension jQuery(async () => { console.log(`[${extensionName}] Initializing...`); try { // Load settings HTML const settingsHtml = await $.get(`${extensionFolderPath}/example.html`); // Visual extensions go in the right column (extensions_settings2) $("#extensions_settings2").append(settingsHtml); // Register event listeners $("#sillybubble_enabled").on("input", onEnabledInput); $("#sillybubble_image_url").on("input", onImageUrlInput); $("#sillybubble_default_style").on("input", onDefaultStyleInput); $("#sillybubble_test_button").on("click", onTestButtonClick); // Wait for the document to fully load before trying to register tools $(document).ready(() => { setTimeout(() => { // Try to register the function tool after a short delay // to ensure all SillyTavern systems are initialized registerFunctionTool(); // Also add the function to extension context try { const context = getContext(); if (context) { // Make the function available to the AI context.generateChatBubbleImage = generateChatBubbleImage; console.log(`[${extensionName}] Function added to context: generateChatBubbleImage`); } } catch (error) { console.error(`[${extensionName}] Error adding function to context:`, error); } }, 2000); // Wait 2 seconds for SillyTavern to initialize }); // Load settings loadSettings(); console.log(`[${extensionName}] Initialization complete`); } catch (error) { console.error(`[${extensionName}] Initialization error:`, error); } });