Add PHP image generator and documentation
- Create image.php script for generating bubble images - Add example usage HTML page for testing - Update README with documentation - Support multiple bubble styles - Gracefully handle missing fonts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
790f7a33b7
commit
53db325bb2
86
README.md
86
README.md
@ -1,42 +1,76 @@
|
||||
# SillyBubble - Dynamic Chat Bubble Extension for SillyTavern
|
||||
# SillyBubble Chat Bubble Generator
|
||||
|
||||
SillyBubble is a SillyTavern extension that enables AI models to generate dynamic chat bubble images with text. The extension provides a function that AI models can call to create properly formatted markdown image links with URL-encoded text parameters.
|
||||
SillyBubble is a package that includes both a SillyTavern extension for generating chat bubble images from AI responses and a PHP script for rendering those bubbles.
|
||||
|
||||
## Features
|
||||
## Components
|
||||
|
||||
- Provides a function for AI to generate markdown image links
|
||||
- Properly URL-encodes text parameters for image generation
|
||||
- Supports different bubble styles through style parameters
|
||||
- Configurable image service URL
|
||||
- Simple testing interface to preview bubble generation
|
||||
1. **SillyTavern Extension**: JavaScript extension that registers a function tool for AIs to generate bubble images
|
||||
2. **PHP Image Generator**: Server-side script that creates the actual images
|
||||
|
||||
## Installation and Usage
|
||||
## Chat Bubble Image Generator (image.php)
|
||||
|
||||
### Installation
|
||||
The `image.php` script is a standalone PHP application that generates chat bubble images from text. It uses the GD library to create PNG images dynamically.
|
||||
|
||||
1. Install the extension using SillyTavern's built-in extension installer
|
||||
2. Configure the image service URL in the extension settings
|
||||
### Requirements
|
||||
|
||||
- PHP 7.0+ with GD extension enabled
|
||||
- Web server (Apache, Nginx, etc.)
|
||||
|
||||
### Usage
|
||||
|
||||
1. The extension registers a custom function `generateChatBubbleImage` that AI models can call
|
||||
2. When called, it returns a properly formatted markdown image link:
|
||||
```markdown
|
||||

|
||||
```
|
||||
3. The function accepts two parameters:
|
||||
- `text`: The text to display in the bubble (required)
|
||||
- `style`: The visual style of the bubble (optional)
|
||||
The script accepts the following GET parameters:
|
||||
|
||||
## Prerequisites
|
||||
- `q`: The text to display (URL-encoded)
|
||||
- `style`: (Optional) The bubble style: `default`, `modern`, `retro`, or `minimal`
|
||||
|
||||
- SillyTavern with custom function support
|
||||
- A properly configured image generation service that accepts text parameters
|
||||
Example URLs:
|
||||
```
|
||||
image.php?q=Hello%20World
|
||||
image.php?q=Hello%20World&style=modern
|
||||
```
|
||||
|
||||
## Support and Contributions
|
||||
### Styles
|
||||
|
||||
For support or to contribute to this project, please visit the [GitHub repository](https://github.com/crystal/SillyBubble) or open an issue.
|
||||
The image generator comes with several built-in styles:
|
||||
|
||||
- **default**: Light gray background with dark text
|
||||
- **modern**: Blue background with white text
|
||||
- **retro**: Yellow background with dark text
|
||||
- **minimal**: White background with black text and no rounded corners
|
||||
|
||||
### Integrating with SillyBubble Extension
|
||||
|
||||
1. Place the `image.php` script on your web server
|
||||
2. Configure the SillyBubble extension to use the correct URL to your image.php script
|
||||
3. In the extension settings, set the "Image Service URL" to the path to your image.php script
|
||||
|
||||
### Customizing
|
||||
|
||||
You can customize the styles by editing the `$styles` array in the PHP script. Each style has parameters for:
|
||||
|
||||
- `bg_color`: Background color [R, G, B]
|
||||
- `text_color`: Text color [R, G, B]
|
||||
- `border_color`: Border color [R, G, B]
|
||||
- `padding`: Padding around the text
|
||||
- `rounded`: Corner radius (0 for square corners)
|
||||
- `font_size`: Text size
|
||||
- `max_width`: Maximum bubble width
|
||||
- `line_height`: Spacing between lines
|
||||
- `font`: Path to a TTF font file
|
||||
|
||||
### Fonts
|
||||
|
||||
The script will look for TTF fonts in the `fonts` directory. If no fonts are found, it will fall back to using the built-in GD fonts.
|
||||
|
||||
To add custom fonts:
|
||||
1. Create a `fonts` directory in the same location as the script
|
||||
2. Add `.ttf` font files to this directory
|
||||
3. Update the font paths in the `$styles` array if needed
|
||||
|
||||
## Example/Demo Page
|
||||
|
||||
The included `example-usage.html` file provides a simple interface for testing the image generator with different styles and messages.
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
This project is licensed under the MIT License.
|
152
example-usage.html
Normal file
152
example-usage.html
Normal file
@ -0,0 +1,152 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>SillyBubble Example</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.example {
|
||||
border: 1px solid #ddd;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.example h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.example-images {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.example-images img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
code {
|
||||
background-color: #f5f5f5;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.style-selector {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
label {
|
||||
margin-right: 10px;
|
||||
}
|
||||
#message {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#preview-image {
|
||||
margin-top: 20px;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SillyBubble Image Generator Examples</h1>
|
||||
|
||||
<div class="example">
|
||||
<h2>Try It Yourself</h2>
|
||||
<div>
|
||||
<textarea id="message" rows="4" placeholder="Enter your message here...">Hello, I am a chat bubble! Try me with different styles!</textarea>
|
||||
|
||||
<div class="style-selector">
|
||||
<label>
|
||||
<input type="radio" name="style" value="default" checked> Default
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="style" value="modern"> Modern
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="style" value="retro"> Retro
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="style" value="minimal"> Minimal
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button id="generate-btn">Generate Bubble</button>
|
||||
|
||||
<div>
|
||||
<img id="preview-image" src="" alt="Preview will appear here">
|
||||
</div>
|
||||
|
||||
<div id="markdown-output" style="margin-top: 20px;">
|
||||
<strong>Markdown for this image:</strong>
|
||||
<pre><code id="markdown-code"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<h2>Examples of Different Styles</h2>
|
||||
<div class="example-images">
|
||||
<div>
|
||||
<p><strong>Default:</strong></p>
|
||||
<img src="image.php?q=This%20is%20the%20default%20style%20for%20chat%20bubbles.%20It%20has%20a%20light%20gray%20background%20with%20rounded%20corners." alt="Default style">
|
||||
</div>
|
||||
<div>
|
||||
<p><strong>Modern:</strong></p>
|
||||
<img src="image.php?q=This%20is%20the%20modern%20style.%20It%20features%20a%20blue%20background%20with%20white%20text%20for%20a%20contemporary%20look.&style=modern" alt="Modern style">
|
||||
</div>
|
||||
<div>
|
||||
<p><strong>Retro:</strong></p>
|
||||
<img src="image.php?q=This%20is%20the%20retro%20style%20with%20a%20yellow%20background.%20It%20has%20slightly%20less%20rounded%20corners%20for%20that%20old-school%20feel.&style=retro" alt="Retro style">
|
||||
</div>
|
||||
<div>
|
||||
<p><strong>Minimal:</strong></p>
|
||||
<img src="image.php?q=This%20is%20the%20minimal%20style.%20It%20has%20a%20white%20background%20with%20no%20rounded%20corners%20for%20a%20clean,%20simple%20look.&style=minimal" alt="Minimal style">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="example">
|
||||
<h2>Usage in SillyBubble Extension</h2>
|
||||
<p>To use this image generator with the SillyBubble extension, make sure your extension settings point to this script:</p>
|
||||
<pre><code>// In SillyBubble settings
|
||||
"image_service_url": "image.php"</code></pre>
|
||||
|
||||
<p>The extension will automatically generate URLs like:</p>
|
||||
<pre><code></code></pre>
|
||||
|
||||
<p>When the AI uses the function, it will create these markdown images that browsers will render as chat bubbles.</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('generate-btn').addEventListener('click', function() {
|
||||
const message = document.getElementById('message').value;
|
||||
const style = document.querySelector('input[name="style"]:checked').value;
|
||||
|
||||
const encodedMessage = encodeURIComponent(message);
|
||||
const imageUrl = `image.php?q=${encodedMessage}${style !== 'default' ? '&style=' + style : ''}`;
|
||||
|
||||
document.getElementById('preview-image').src = imageUrl;
|
||||
|
||||
// Update markdown
|
||||
const markdown = ``;
|
||||
document.getElementById('markdown-code').textContent = markdown;
|
||||
});
|
||||
|
||||
// Initialize with default text
|
||||
document.getElementById('generate-btn').click();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
221
image.php
Normal file
221
image.php
Normal file
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
/**
|
||||
* SillyBubble Image Generator
|
||||
*
|
||||
* This script generates chat bubble images from text.
|
||||
* It takes a 'q' parameter for the text content and an optional 'style' parameter.
|
||||
*
|
||||
* Usage:
|
||||
* image.php?q=Hello+World
|
||||
* image.php?q=Hello+World&style=modern
|
||||
*/
|
||||
|
||||
// Set content type to PNG image
|
||||
header('Content-Type: image/png');
|
||||
|
||||
// Get parameters
|
||||
$text = isset($_GET['q']) ? urldecode($_GET['q']) : 'No text provided';
|
||||
$style = isset($_GET['style']) ? $_GET['style'] : 'default';
|
||||
|
||||
// Define styles
|
||||
$styles = [
|
||||
'default' => [
|
||||
'bg_color' => [245, 245, 245],
|
||||
'text_color' => [50, 50, 50],
|
||||
'border_color' => [200, 200, 200],
|
||||
'padding' => 20,
|
||||
'rounded' => 15,
|
||||
'font_size' => 14,
|
||||
'max_width' => 600,
|
||||
'line_height' => 20,
|
||||
'font' => __DIR__ . '/fonts/arial.ttf', // Adjust path as needed
|
||||
],
|
||||
'modern' => [
|
||||
'bg_color' => [66, 133, 244],
|
||||
'text_color' => [255, 255, 255],
|
||||
'border_color' => [59, 120, 220],
|
||||
'padding' => 20,
|
||||
'rounded' => 20,
|
||||
'font_size' => 14,
|
||||
'max_width' => 600,
|
||||
'line_height' => 20,
|
||||
'font' => __DIR__ . '/fonts/arial.ttf', // Adjust path as needed
|
||||
],
|
||||
'retro' => [
|
||||
'bg_color' => [255, 204, 102],
|
||||
'text_color' => [51, 51, 51],
|
||||
'border_color' => [204, 153, 0],
|
||||
'padding' => 20,
|
||||
'rounded' => 5,
|
||||
'font_size' => 14,
|
||||
'max_width' => 600,
|
||||
'line_height' => 20,
|
||||
'font' => __DIR__ . '/fonts/arial.ttf', // Adjust path as needed
|
||||
],
|
||||
'minimal' => [
|
||||
'bg_color' => [255, 255, 255],
|
||||
'text_color' => [0, 0, 0],
|
||||
'border_color' => [220, 220, 220],
|
||||
'padding' => 15,
|
||||
'rounded' => 0,
|
||||
'font_size' => 14,
|
||||
'max_width' => 600,
|
||||
'line_height' => 20,
|
||||
'font' => __DIR__ . '/fonts/arial.ttf', // Adjust path as needed
|
||||
],
|
||||
];
|
||||
|
||||
// Use default style if specified style doesn't exist
|
||||
if (!isset($styles[$style])) {
|
||||
$style = 'default';
|
||||
}
|
||||
|
||||
// Get style settings
|
||||
$config = $styles[$style];
|
||||
|
||||
// Check for fonts directory and create if it doesn't exist
|
||||
$fontsDir = __DIR__ . '/fonts';
|
||||
if (!is_dir($fontsDir)) {
|
||||
mkdir($fontsDir, 0755, true);
|
||||
}
|
||||
|
||||
// Fallback to a built-in font if the specified font file doesn't exist
|
||||
if (!file_exists($config['font'])) {
|
||||
// Try to find any TTF font in the fonts directory
|
||||
$foundFont = false;
|
||||
if (is_dir($fontsDir)) {
|
||||
$fontFiles = glob($fontsDir . '/*.ttf');
|
||||
if (!empty($fontFiles)) {
|
||||
$config['font'] = $fontFiles[0];
|
||||
$foundFont = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If no TTF font found, use built-in font
|
||||
if (!$foundFont) {
|
||||
$config['use_builtin_font'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary image to calculate text dimensions
|
||||
$tempImage = imagecreatetruecolor(100, 100);
|
||||
$textColor = imagecolorallocate($tempImage, $config['text_color'][0], $config['text_color'][1], $config['text_color'][2]);
|
||||
|
||||
// Word wrap the text
|
||||
$words = explode(' ', $text);
|
||||
$lines = [];
|
||||
$currentLine = '';
|
||||
$maxWidth = $config['max_width'] - (2 * $config['padding']);
|
||||
|
||||
foreach ($words as $word) {
|
||||
$testLine = $currentLine . ' ' . $word;
|
||||
$testLine = ltrim($testLine); // Remove leading space
|
||||
|
||||
// Calculate width using built-in or TTF font
|
||||
if (isset($config['use_builtin_font'])) {
|
||||
$textWidth = imagefontwidth(5) * strlen($testLine);
|
||||
} else {
|
||||
$bbox = imagettfbbox($config['font_size'], 0, $config['font'], $testLine);
|
||||
$textWidth = $bbox[2] - $bbox[0];
|
||||
}
|
||||
|
||||
if ($textWidth > $maxWidth && $currentLine !== '') {
|
||||
$lines[] = $currentLine;
|
||||
$currentLine = $word;
|
||||
} else {
|
||||
$currentLine = $testLine;
|
||||
}
|
||||
}
|
||||
if ($currentLine !== '') {
|
||||
$lines[] = $currentLine;
|
||||
}
|
||||
|
||||
// Calculate image dimensions
|
||||
$lineCount = count($lines);
|
||||
if (isset($config['use_builtin_font'])) {
|
||||
$lineHeight = imagefontheight(5);
|
||||
$textHeight = $lineHeight * $lineCount;
|
||||
|
||||
// Find the longest line to determine width
|
||||
$textWidth = 0;
|
||||
foreach ($lines as $line) {
|
||||
$lineWidth = imagefontwidth(5) * strlen($line);
|
||||
$textWidth = max($textWidth, $lineWidth);
|
||||
}
|
||||
} else {
|
||||
$lineHeight = $config['line_height'];
|
||||
$textHeight = $lineHeight * $lineCount;
|
||||
|
||||
// Find the longest line to determine width
|
||||
$textWidth = 0;
|
||||
foreach ($lines as $line) {
|
||||
$bbox = imagettfbbox($config['font_size'], 0, $config['font'], $line);
|
||||
$lineWidth = $bbox[2] - $bbox[0];
|
||||
$textWidth = max($textWidth, $lineWidth);
|
||||
}
|
||||
}
|
||||
|
||||
// Add padding to dimensions
|
||||
$imgWidth = min($config['max_width'], $textWidth + (2 * $config['padding']));
|
||||
$imgHeight = $textHeight + (2 * $config['padding']);
|
||||
|
||||
// Create the bubble image
|
||||
$image = imagecreatetruecolor($imgWidth, $imgHeight);
|
||||
|
||||
// Allocate colors
|
||||
$bgColor = imagecolorallocate($image, $config['bg_color'][0], $config['bg_color'][1], $config['bg_color'][2]);
|
||||
$borderColor = imagecolorallocate($image, $config['border_color'][0], $config['border_color'][1], $config['border_color'][2]);
|
||||
$textColor = imagecolorallocate($image, $config['text_color'][0], $config['text_color'][1], $config['text_color'][2]);
|
||||
|
||||
// Fill background
|
||||
imagefill($image, 0, 0, $bgColor);
|
||||
|
||||
// Draw rounded rectangle (if rounded corners are requested)
|
||||
if ($config['rounded'] > 0) {
|
||||
// Draw filled rectangle
|
||||
imagefilledrectangle($image, 0, 0, $imgWidth - 1, $imgHeight - 1, $bgColor);
|
||||
|
||||
// Draw rounded corners - basic simulation for rounded corners
|
||||
// This could be improved with proper arc drawing
|
||||
$r = $config['rounded'];
|
||||
|
||||
// Top left corner
|
||||
imagefilledarc($image, $r, $r, $r * 2, $r * 2, 180, 270, $bgColor, IMG_ARC_PIE);
|
||||
|
||||
// Top right corner
|
||||
imagefilledarc($image, $imgWidth - $r - 1, $r, $r * 2, $r * 2, 270, 360, $bgColor, IMG_ARC_PIE);
|
||||
|
||||
// Bottom left corner
|
||||
imagefilledarc($image, $r, $imgHeight - $r - 1, $r * 2, $r * 2, 90, 180, $bgColor, IMG_ARC_PIE);
|
||||
|
||||
// Bottom right corner
|
||||
imagefilledarc($image, $imgWidth - $r - 1, $imgHeight - $r - 1, $r * 2, $r * 2, 0, 90, $bgColor, IMG_ARC_PIE);
|
||||
|
||||
// Draw border
|
||||
imagerectangle($image, 0, 0, $imgWidth - 1, $imgHeight - 1, $borderColor);
|
||||
} else {
|
||||
// Draw simple rectangle
|
||||
imagefilledrectangle($image, 0, 0, $imgWidth - 1, $imgHeight - 1, $bgColor);
|
||||
imagerectangle($image, 0, 0, $imgWidth - 1, $imgHeight - 1, $borderColor);
|
||||
}
|
||||
|
||||
// Draw text
|
||||
$y = $config['padding'];
|
||||
foreach ($lines as $line) {
|
||||
if (isset($config['use_builtin_font'])) {
|
||||
// Use built-in font (less nice but always available)
|
||||
imagestring($image, 5, $config['padding'], $y, $line, $textColor);
|
||||
$y += imagefontheight(5);
|
||||
} else {
|
||||
// Use TrueType font (nicer but requires font file)
|
||||
imagettftext($image, $config['font_size'], 0, $config['padding'], $y + $config['font_size'], $textColor, $config['font'], $line);
|
||||
$y += $lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Output the image
|
||||
imagepng($image);
|
||||
|
||||
// Clean up
|
||||
imagedestroy($image);
|
||||
imagedestroy($tempImage);
|
Loading…
x
Reference in New Issue
Block a user