Update SillyBubble extension to support new image features

- Add support for character-based bubble styling
- Add support for speech and thought bubble types
- Update function schema with new parameters
- Add UI settings for character and bubble type selection
- Update examples and documentation
- Keep backward compatibility with style parameter

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sven Olderaan 2025-03-16 16:13:48 +01:00
parent e0d54bd1b6
commit 48610c7ba6
2 changed files with 83 additions and 18 deletions

View File

@ -21,7 +21,7 @@
</div>
<div class="sillybubble_block flex-container">
<label for="sillybubble_default_style">Default Bubble Style:</label>
<label for="sillybubble_default_style">Default Bubble Style (Legacy):</label>
<select id="sillybubble_default_style" class="text_pole">
<option value="default">Default</option>
<option value="modern">Modern</option>
@ -30,6 +30,22 @@
</select>
</div>
<div class="sillybubble_block flex-container">
<label for="sillybubble_default_character">Default Character:</label>
<select id="sillybubble_default_character" class="text_pole">
<option value="Example">Example</option>
<option value="Bianca">Bianca</option>
</select>
</div>
<div class="sillybubble_block flex-container">
<label for="sillybubble_default_bubble_type">Default Bubble Type:</label>
<select id="sillybubble_default_bubble_type" class="text_pole">
<option value="speech">Speech</option>
<option value="thought">Thought</option>
</select>
</div>
<div class="sillybubble_block flex-container">
<input id="sillybubble_test_button" class="menu_button" type="submit" value="Test Chat Bubble" />
</div>
@ -40,7 +56,9 @@
<p>Parameters:</p>
<ul>
<li><code>text</code>: The text to display in the bubble</li>
<li><code>style</code>: (Optional) Bubble style</li>
<li><code>character</code>: (Optional) Character to use (Example, Bianca, etc.)</li>
<li><code>bubble_type</code>: (Optional) Bubble type (speech, thought)</li>
<li><code>style</code>: (Optional, Legacy) Bubble style</li>
</ul>
<div class="sillybubble_important">
<p><strong>Important:</strong> Make sure function calling is enabled in SillyTavern's AI settings and the model you're using supports function calling.</p>
@ -51,16 +69,20 @@
<li>The function appears in the function list</li>
</ol>
<p><strong>🚨 REQUIRED SETUP:</strong> You MUST add the following to your system prompt or character card:</p>
<pre>You have access to a function called generateChatBubbleImage(text, style) that creates chat bubbles with the given text.
<pre>You have access to a function called generateChatBubbleImage(text, character, bubble_type, style) that creates character-styled chat bubbles.
The function parameters are:
- text: The text to display in the chat bubble (required string)
- style: Visual style of the bubble (optional string: "default", "modern", "retro", or "minimal")
- character: The character to use for the bubble (optional string: "Example", "Bianca", etc.)
- bubble_type: The type of bubble to use (optional string: "speech" or "thought")
- style: Legacy parameter for visual style (optional string: "default", "modern", "retro", or "minimal")
When you want to create a chat bubble, call this function with the text you want to display.
Example usage:
generateChatBubbleImage("Hello world!")
generateChatBubbleImage("Hello in retro style", "retro")</pre>
generateChatBubbleImage("Hello from Bianca", "Bianca")
generateChatBubbleImage("I'm thinking...", "Example", "thought")
generateChatBubbleImage("Hello in retro style", null, null, "retro")</pre>
<p><strong>ALTERNATIVE:</strong> If the AI can't call the function directly, you can instruct it to respond with Markdown formatted like this:</p>
<pre>![](image.php?q=Hello%20world)</pre>
</div>

View File

@ -10,6 +10,8 @@ const extensionFolderPath = `scripts/extensions/third-party/${extensionName}`;
const defaultSettings = {
image_service_url: "http://calista.the.sexiest.cat/image.php", // Use fully qualified URL by default
default_style: "default",
default_character: "Example",
default_bubble_type: "speech",
enabled: true,
render_in_collapse: true // Setting to enable/disable rendering in collapsed tool calls
};
@ -33,13 +35,15 @@ if (extension_settings[extensionName].image_service_url &&
}
// Function for AI to call - generates markdown image with URL-encoded text
function generateChatBubbleImage(text, style) {
function generateChatBubbleImage(text, style, character, bubble_type) {
if (!extension_settings[extensionName].enabled) {
return text;
}
// Use default style if not specified
// Use default values if parameters not specified
const bubbleStyle = style || extension_settings[extensionName].default_style;
const characterName = character || extension_settings[extensionName].default_character;
const bubbleType = bubble_type || extension_settings[extensionName].default_bubble_type;
// URL encode the text parameter
const encodedText = encodeURIComponent(text);
@ -52,17 +56,26 @@ function generateChatBubbleImage(text, style) {
}
// Construct the URL with the encoded text
const imageUrl = `${serviceUrl}?q=${encodedText}`;
let imageUrl = `${serviceUrl}?q=${encodedText}`;
// Add style parameter if specified
const fullUrl = bubbleStyle !== "default"
? `${imageUrl}&style=${bubbleStyle}`
: imageUrl;
// Add parameters if they differ from defaults
if (characterName && characterName !== "Example") {
imageUrl += `&character=${characterName}`;
}
console.log(`[${extensionName}] Generated image URL: ${fullUrl}`);
if (bubbleType && bubbleType !== "speech") {
imageUrl += `&bubble_type=${bubbleType}`;
}
// Add style parameter if specified (for backward compatibility)
if (bubbleStyle && bubbleStyle !== "default" && !characterName) {
imageUrl += `&style=${bubbleStyle}`;
}
console.log(`[${extensionName}] Generated image URL: ${imageUrl}`);
// Return markdown image format
return `![](${fullUrl})`;
return `![](${imageUrl})`;
}
// Load extension settings into UI
@ -71,6 +84,8 @@ function loadSettings() {
$("#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");
$("#sillybubble_default_character").val(extension_settings[extensionName].default_character).trigger("input");
$("#sillybubble_default_bubble_type").val(extension_settings[extensionName].default_bubble_type).trigger("input");
$("#sillybubble_render_in_collapse").prop("checked", extension_settings[extensionName].render_in_collapse).trigger("input");
}
@ -103,6 +118,20 @@ function onDefaultStyleInput(event) {
saveSettingsDebounced();
}
// Handle default character changes
function onDefaultCharacterInput(event) {
const value = $(event.target).val();
extension_settings[extensionName].default_character = value;
saveSettingsDebounced();
}
// Handle default bubble type changes
function onDefaultBubbleTypeInput(event) {
const value = $(event.target).val();
extension_settings[extensionName].default_bubble_type = value;
saveSettingsDebounced();
}
// Handle render in collapse toggle
function onRenderInCollapseInput(event) {
const value = Boolean($(event.target).prop("checked"));
@ -116,11 +145,15 @@ function onRenderInCollapseInput(event) {
// Test function to visualize a bubble
function onTestButtonClick() {
const testText = "This is a test chat bubble generated by SillyBubble!";
const markdown = generateChatBubbleImage(testText);
const character = extension_settings[extensionName].default_character;
const bubbleType = extension_settings[extensionName].default_bubble_type;
const markdown = generateChatBubbleImage(testText, null, character, bubbleType);
const testPopup = `
<div class="sillybubble-test">
<p>Generated Markdown:</p>
<pre>${markdown.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</pre>
<p>Using character: <strong>${character}</strong>, bubble type: <strong>${bubbleType}</strong></p>
<p>Preview (if connected to image service):</p>
<div>${markdown}</div>
</div>
@ -151,7 +184,15 @@ function registerFunctionTool() {
},
style: {
type: 'string',
description: 'The visual style of the chat bubble (default, modern, retro, minimal)'
description: 'Legacy parameter: The visual style of the chat bubble (default, modern, retro, minimal). Prefer using character parameter instead.'
},
character: {
type: 'string',
description: 'The character to display (Example, Bianca, etc.). Each character has its own bubble style.'
},
bubble_type: {
type: 'string',
description: 'The type of bubble to use (speech, thought). Defaults to speech.'
},
},
required: ['text']
@ -161,11 +202,11 @@ function registerFunctionTool() {
context.registerFunctionTool({
name: 'generateChatBubbleImage',
displayName: 'Chat Bubble Image',
description: 'Creates a markdown image link with the text displayed as a styled chat bubble. Use when you want to visually style messages or display text in a speech bubble.',
description: 'Creates a markdown image link with the text displayed as a character-styled chat bubble. Use when you want to visually style messages or display text in a speech or thought bubble.',
parameters: bubbleSchema,
action: async (args) => {
if (!args?.text) return '';
return generateChatBubbleImage(args.text, args.style);
return generateChatBubbleImage(args.text, args.style, args.character, args.bubble_type);
},
formatMessage: () => '',
});
@ -395,6 +436,8 @@ jQuery(async () => {
$("#sillybubble_enabled").on("input", onEnabledInput);
$("#sillybubble_image_url").on("input", onImageUrlInput);
$("#sillybubble_default_style").on("input", onDefaultStyleInput);
$("#sillybubble_default_character").on("input", onDefaultCharacterInput);
$("#sillybubble_default_bubble_type").on("input", onDefaultBubbleTypeInput);
$("#sillybubble_render_in_collapse").on("input", onRenderInCollapseInput);
$("#sillybubble_test_button").on("click", onTestButtonClick);