feat: Add plugin configuration editing with template variable parsing
## Backend Changes:
- Enhanced PluginTemplate.save() to auto-parse template variables from plugin code
- Updated PluginInstallationSerializer to expose template metadata (description, category, version, author, logo, template_variables)
- Fixed template variable parser to handle nested {{ }} braces in default values
- Added brace-counting algorithm to properly extract variables with insertion codes
- Fixed explicit type parameter detection (textarea, text, email, etc.)
- Made scheduled_task optional on PluginInstallation model
- Added EventPlugin through model for event-plugin relationships
- Added Event.execute_plugins() method for plugin automation
## Frontend Changes:
- Created Tasks.tsx page for managing scheduled tasks
- Enhanced MyPlugins page with clickable plugin cards
- Added edit configuration modal with dynamic form generation
- Implemented escape sequence handling (convert \n, \', etc. for display)
- Added plugin logos to My Plugins page
- Updated type definitions for PluginInstallation interface
- Added insertion code documentation to Plugin Docs
## Plugin System:
- All platform plugins now have editable email templates with textarea support
- Template variables properly parsed with full default values
- Insertion codes ({{CUSTOMER_NAME}}, {{BUSINESS_NAME}}, etc.) documented
- Plugin logos displayed in marketplace and My Plugins
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
BIN
frontend/public/plugin-logos/appointment-reminder-24hr.png
Normal file
|
After Width: | Height: | Size: 993 B |
1
frontend/public/plugin-logos/appointment_reminder.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-bell"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
|
||||
|
After Width: | Height: | Size: 299 B |
1
frontend/public/plugin-logos/backup_database.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-database"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s 9-1.34 9-3V5"></path></svg>
|
||||
|
After Width: | Height: | Size: 351 B |
BIN
frontend/public/plugin-logos/birthday-greetings.png
Normal file
|
After Width: | Height: | Size: 840 B |
1
frontend/public/plugin-logos/cleanup_old_events.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-trash-2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
||||
|
After Width: | Height: | Size: 426 B |
BIN
frontend/public/plugin-logos/daily-appointment-summary.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
1
frontend/public/plugin-logos/daily_report.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file-text"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 451 B |
132
frontend/public/plugin-logos/generate_all_logos_gemini.py
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
from google import genai
|
||||
from google.genai import types
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
import os
|
||||
import time
|
||||
|
||||
# Setup Client
|
||||
client = genai.Client(api_key="AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw")
|
||||
|
||||
OUTPUT_DIR = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos"
|
||||
|
||||
# Plugin configurations with detailed prompts
|
||||
plugins = [
|
||||
{
|
||||
'filename': 'daily-appointment-summary.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in square format with rounded corners.
|
||||
Design: Indigo blue gradient background (#4f46e5 to lighter blue).
|
||||
Icon: Simple white envelope overlaid with a small calendar icon in the corner.
|
||||
Style: Flat design, clean geometric shapes, professional SaaS aesthetic.
|
||||
The icon should be crisp and recognizable at 48x48 pixels.'''
|
||||
},
|
||||
{
|
||||
'filename': 'no-show-tracker.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in square format with rounded corners.
|
||||
Design: Red gradient background (#dc2626 to darker red).
|
||||
Icon: Simple white person silhouette with a bold white X symbol overlaid.
|
||||
Style: Flat design, clean geometric shapes, professional warning aesthetic.
|
||||
The icon should clearly convey "missed appointment" at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'birthday-greetings.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in square format with rounded corners.
|
||||
Design: Pink gradient background (#ec4899 to lighter pink).
|
||||
Icon: Simple white birthday cake with 3 candles or a white gift box with a bow.
|
||||
Style: Flat design, clean geometric shapes, cheerful yet professional aesthetic.
|
||||
The icon should feel celebratory and friendly at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'monthly-revenue-report.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in square format with rounded corners.
|
||||
Design: Green gradient background (#10b981 to darker green).
|
||||
Icon: Simple white upward trending line chart or ascending bar graph.
|
||||
Style: Flat design, clean geometric shapes, professional business analytics aesthetic.
|
||||
The icon should convey growth and success at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'appointment-reminder-24hr.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in square format with rounded corners.
|
||||
Design: Amber/orange gradient background (#f59e0b to darker orange).
|
||||
Icon: Simple white notification bell with a small red circular alert badge.
|
||||
Style: Flat design, clean geometric shapes, attention-grabbing yet professional aesthetic.
|
||||
The icon should clearly indicate alerts and reminders at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'inactive-customer-reengagement.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in square format with rounded corners.
|
||||
Design: Purple gradient background (#8b5cf6 to darker purple).
|
||||
Icon: Simple white heart symbol with a circular white refresh/return arrow around it.
|
||||
Style: Flat design, clean geometric shapes, warm and welcoming professional aesthetic.
|
||||
The icon should convey customer care and returning customers at small sizes.'''
|
||||
}
|
||||
]
|
||||
|
||||
def generate_logo(prompt, filename):
|
||||
"""Generate a logo using Gemini 2.5 Flash Image"""
|
||||
print(f"\nGenerating {filename}...")
|
||||
|
||||
try:
|
||||
response = client.models.generate_content(
|
||||
model='gemini-2.5-flash-image',
|
||||
contents=prompt,
|
||||
config=types.GenerateContentConfig(
|
||||
response_modalities=["IMAGE"]
|
||||
)
|
||||
)
|
||||
|
||||
# Save the Image
|
||||
if response.candidates[0].content.parts:
|
||||
for part in response.candidates[0].content.parts:
|
||||
if part.inline_data:
|
||||
image_data = part.inline_data.data
|
||||
image = Image.open(BytesIO(image_data))
|
||||
|
||||
# Save to output directory
|
||||
output_path = os.path.join(OUTPUT_DIR, filename)
|
||||
image.save(output_path)
|
||||
print(f"✓ Saved: {output_path}")
|
||||
return True
|
||||
else:
|
||||
print(f"✗ Model returned text instead of image: {part.text[:100]}")
|
||||
return False
|
||||
else:
|
||||
print("✗ No content parts in response")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if "RESOURCE_EXHAUSTED" in error_msg or "quota" in error_msg.lower():
|
||||
print(f"✗ Quota exceeded. Please try again tomorrow when quota resets.")
|
||||
print(f" Error: {error_msg[:200]}...")
|
||||
else:
|
||||
print(f"✗ Error: {type(e).__name__}: {error_msg[:200]}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
print("=" * 70)
|
||||
print("Generating Plugin Logos with Gemini 2.5 Flash Image")
|
||||
print("=" * 70)
|
||||
|
||||
success_count = 0
|
||||
for i, plugin in enumerate(plugins):
|
||||
if i > 0:
|
||||
# Wait 3 seconds between requests to avoid rate limiting
|
||||
print("\nWaiting 3 seconds before next request...")
|
||||
time.sleep(3)
|
||||
|
||||
if generate_logo(plugin['prompt'], plugin['filename']):
|
||||
success_count += 1
|
||||
|
||||
print("\n" + "=" * 70)
|
||||
print(f"Generation complete: {success_count}/{len(plugins)} successful")
|
||||
print("=" * 70)
|
||||
|
||||
if success_count == 0:
|
||||
print("\nNote: If you hit quota limits, try again tomorrow when the quota resets!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
77
frontend/public/plugin-logos/generate_daily_summary.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw
|
||||
except ImportError:
|
||||
print("Error: Pillow library not found.")
|
||||
print("Please install it using: pip install Pillow")
|
||||
sys.exit(1)
|
||||
|
||||
# Configuration
|
||||
OUTPUT_PATH = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos/daily-appointment-summary.png"
|
||||
SIZE = (144, 144) # Generated at 3x resolution (144px) for high quality on 48px displays
|
||||
BG_COLOR = "#4f46e5" # Indigo/Blue
|
||||
ICON_COLOR = "white"
|
||||
|
||||
def create_logo():
|
||||
# Create a new image with a transparent background
|
||||
img = Image.new('RGBA', SIZE, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
# 1. Draw Background (Rounded Square)
|
||||
radius = 30
|
||||
rect_bounds = [0, 0, SIZE[0], SIZE[1]]
|
||||
draw.rounded_rectangle(rect_bounds, radius=radius, fill=BG_COLOR)
|
||||
|
||||
# 2. Draw Envelope Icon (Email concept)
|
||||
# Centered, slightly offset up to make room for calendar
|
||||
env_w = 90
|
||||
env_h = 60
|
||||
env_x = (SIZE[0] - env_w) // 2
|
||||
env_y = (SIZE[1] - env_h) // 2 - 10
|
||||
|
||||
# Envelope body
|
||||
draw.rectangle([env_x, env_y, env_x + env_w, env_y + env_h], outline=ICON_COLOR, width=5)
|
||||
|
||||
# Envelope flap (V shape)
|
||||
draw.line([env_x, env_y, env_x + env_w // 2, env_y + env_h // 2 + 5], fill=ICON_COLOR, width=5)
|
||||
draw.line([env_x + env_w, env_y, env_x + env_w // 2, env_y + env_h // 2 + 5], fill=ICON_COLOR, width=5)
|
||||
|
||||
# 3. Draw Calendar Badge (Scheduling concept)
|
||||
# Bottom right corner
|
||||
cal_size = 50
|
||||
cal_x = env_x + env_w - (cal_size // 2)
|
||||
cal_y = env_y + env_h - (cal_size // 2)
|
||||
|
||||
# Clear background behind calendar for separation
|
||||
border = 4
|
||||
draw.rounded_rectangle(
|
||||
[cal_x - border, cal_y - border, cal_x + cal_size + border, cal_y + cal_size + border],
|
||||
radius=10, fill=BG_COLOR
|
||||
)
|
||||
|
||||
# Calendar body
|
||||
draw.rounded_rectangle([cal_x, cal_y, cal_x + cal_size, cal_y + cal_size], radius=8, fill="white")
|
||||
|
||||
# Calendar red header (using a lighter indigo/blue to match theme)
|
||||
header_h = 14
|
||||
draw.rounded_rectangle(
|
||||
[cal_x, cal_y, cal_x + cal_size, cal_y + header_h],
|
||||
radius=8, corners=(True, True, False, False), fill="#818cf8"
|
||||
)
|
||||
|
||||
# Calendar 'lines'
|
||||
line_x = cal_x + 10
|
||||
line_w = cal_size - 20
|
||||
draw.line([line_x, cal_y + 22, line_x + line_w, cal_y + 22], fill=BG_COLOR, width=3)
|
||||
draw.line([line_x, cal_y + 32, line_x + line_w, cal_y + 32], fill=BG_COLOR, width=3)
|
||||
draw.line([line_x, cal_y + 42, line_x + line_w * 0.6, cal_y + 42], fill=BG_COLOR, width=3)
|
||||
|
||||
# Save the file
|
||||
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
|
||||
img.save(OUTPUT_PATH)
|
||||
print(f"Success! Logo saved to: {OUTPUT_PATH}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_logo()
|
||||
155
frontend/public/plugin-logos/generate_plugin_logos.py
Normal file
@@ -0,0 +1,155 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
except ImportError:
|
||||
print("Error: Pillow library not found.")
|
||||
print("Please install it using: pip install Pillow")
|
||||
sys.exit(1)
|
||||
|
||||
# Configuration
|
||||
OUTPUT_DIR = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos"
|
||||
SIZE = (144, 144)
|
||||
RADIUS = 30
|
||||
|
||||
def create_base_logo(bg_color):
|
||||
"""Creates the base image with rounded corners and background color."""
|
||||
img = Image.new('RGBA', SIZE, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
rect_bounds = [0, 0, SIZE[0], SIZE[1]]
|
||||
draw.rounded_rectangle(rect_bounds, radius=RADIUS, fill=bg_color)
|
||||
return img, draw
|
||||
|
||||
def save_logo(img, filename):
|
||||
"""Saves the image to the output directory."""
|
||||
path = os.path.join(OUTPUT_DIR, filename)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
img.save(path)
|
||||
print(f"Saved: {path}")
|
||||
|
||||
# 1. No-Show Customer Tracker (Red, Person with X)
|
||||
def create_no_show_logo():
|
||||
img, draw = create_base_logo("#dc2626") # Red
|
||||
|
||||
# Person Silhouette
|
||||
cx, cy = 72, 72
|
||||
# Head
|
||||
draw.ellipse([cx-15, 35, cx+15, 65], fill="white")
|
||||
# Shoulders/Body
|
||||
draw.rounded_rectangle([cx-30, 70, cx+30, 130], radius=10, fill="white")
|
||||
|
||||
# Cancel Symbol (Red X on white circle or just overlaid)
|
||||
# Let's use a thick Red X overlay
|
||||
x_center_y = 85
|
||||
half = 20
|
||||
width = 8
|
||||
|
||||
# Draw X
|
||||
draw.line([cx - half, x_center_y - half, cx + half, x_center_y + half], fill="#dc2626", width=width)
|
||||
draw.line([cx + half, x_center_y - half, cx - half, x_center_y + half], fill="#dc2626", width=width)
|
||||
|
||||
save_logo(img, "no-show-tracker.png")
|
||||
|
||||
# 2. Birthday Greeting Campaign (Pink, Gift)
|
||||
def create_birthday_logo():
|
||||
img, draw = create_base_logo("#ec4899") # Pink
|
||||
|
||||
# Gift Box
|
||||
box_w, box_h = 70, 60
|
||||
box_x = (SIZE[0] - box_w) // 2
|
||||
box_y = (SIZE[1] - box_h) // 2 + 10
|
||||
|
||||
# Box body
|
||||
draw.rectangle([box_x, box_y, box_x + box_w, box_y + box_h], fill="white")
|
||||
|
||||
# Ribbons (Lighter Pink)
|
||||
r_w = 12
|
||||
ribbon_color = "#fbcfe8"
|
||||
# Vertical
|
||||
draw.rectangle([box_x + (box_w - r_w)//2, box_y, box_x + (box_w + r_w)//2, box_y + box_h], fill=ribbon_color)
|
||||
# Horizontal
|
||||
draw.rectangle([box_x, box_y + (box_h - r_w)//2, box_x + box_w, box_y + (box_h + r_w)//2], fill=ribbon_color)
|
||||
|
||||
# Bow
|
||||
bow_w = 20
|
||||
draw.ellipse([72 - bow_w, box_y - 15, 72, box_y], fill="white")
|
||||
draw.ellipse([72, box_y - 15, 72 + bow_w, box_y], fill="white")
|
||||
|
||||
save_logo(img, "birthday-greetings.png")
|
||||
|
||||
# 3. Monthly Revenue Report (Green, Chart)
|
||||
def create_revenue_logo():
|
||||
img, draw = create_base_logo("#10b981") # Green
|
||||
|
||||
# Bar Chart
|
||||
margin_bottom = 110
|
||||
bar_w = 20
|
||||
gap = 10
|
||||
start_x = (144 - (3 * bar_w + 2 * gap)) // 2
|
||||
|
||||
# Bars
|
||||
heights = [30, 50, 75]
|
||||
for i, h in enumerate(heights):
|
||||
x = start_x + i * (bar_w + gap)
|
||||
draw.rectangle([x, margin_bottom - h, x + bar_w, margin_bottom], fill="white")
|
||||
|
||||
# Trend Arrow (Rising)
|
||||
arrow_start = (start_x - 5, margin_bottom - 10)
|
||||
arrow_end = (start_x + 3 * bar_w + 2 * gap + 5, margin_bottom - 90)
|
||||
|
||||
# Simple line for trend
|
||||
# draw.line([arrow_start, arrow_end], fill="white", width=4)
|
||||
|
||||
save_logo(img, "monthly-revenue-report.png")
|
||||
|
||||
# 4. Appointment Reminder (24hr) (Amber, Bell)
|
||||
def create_reminder_logo():
|
||||
img, draw = create_base_logo("#f59e0b") # Amber
|
||||
|
||||
cx, cy = 72, 70
|
||||
r = 30
|
||||
|
||||
# Bell Dome
|
||||
draw.chord([cx - r, cy - r, cx + r, cy + r], 180, 0, fill="white")
|
||||
draw.rectangle([cx - r, cy, cx + r, cy + 20], fill="white")
|
||||
|
||||
# Bell Flare
|
||||
draw.polygon([(cx - r, cy + 20), (cx + r, cy + 20), (cx + r + 5, cy + 30), (cx - r - 5, cy + 30)], fill="white")
|
||||
|
||||
# Clapper
|
||||
draw.ellipse([cx - 8, cy + 28, cx + 8, cy + 42], fill="white")
|
||||
|
||||
# Notification Dot
|
||||
draw.ellipse([cx + 15, cy - 25, cx + 35, cy - 5], fill="#dc2626")
|
||||
|
||||
save_logo(img, "appointment-reminder-24hr.png")
|
||||
|
||||
# 5. Inactive Customer Re-engagement (Purple, Heart)
|
||||
def create_inactive_logo():
|
||||
img, draw = create_base_logo("#8b5cf6") # Purple
|
||||
|
||||
hx, hy = 72, 75
|
||||
size = 18
|
||||
|
||||
# Heart Shape
|
||||
# Left circle
|
||||
draw.ellipse([hx - size * 2, hy - size, hx, hy + size], fill="white")
|
||||
# Right circle
|
||||
draw.ellipse([hx, hy - size, hx + size * 2, hy + size], fill="white")
|
||||
# Bottom triangle
|
||||
draw.polygon([(hx - size * 2 + 1, hy + 4), (hx + size * 2 - 1, hy + 4), (hx, hy + size * 2 + 8)], fill="white")
|
||||
|
||||
# Refresh/Loop Arrow (Subtle arc above)
|
||||
bbox = [25, 25, 119, 119]
|
||||
draw.arc(bbox, start=180, end=0, fill="#ddd6fe", width=5)
|
||||
draw.polygon([(119, 72), (110, 60), (128, 60)], fill="#ddd6fe")
|
||||
|
||||
save_logo(img, "inactive-customer-reengagement.png")
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_no_show_logo()
|
||||
create_birthday_logo()
|
||||
create_revenue_logo()
|
||||
create_reminder_logo()
|
||||
create_inactive_logo()
|
||||
39
frontend/public/plugin-logos/generate_with_2_0_flash.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
from google import genai
|
||||
from google.genai import types
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
|
||||
# Setup Client
|
||||
client = genai.Client(api_key="AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw")
|
||||
|
||||
# Test with a simple prompt
|
||||
prompt = "Create a simple modern app icon with a blue background and white envelope symbol, square format, flat design, minimalist"
|
||||
|
||||
print(f"Testing gemini-2.0-flash-exp...")
|
||||
print(f"Prompt: '{prompt}'...")
|
||||
|
||||
try:
|
||||
response = client.models.generate_content(
|
||||
model='gemini-2.0-flash-exp',
|
||||
contents=prompt,
|
||||
config=types.GenerateContentConfig(
|
||||
response_modalities=["IMAGE"]
|
||||
)
|
||||
)
|
||||
|
||||
# Save the Image
|
||||
if response.candidates[0].content.parts:
|
||||
for part in response.candidates[0].content.parts:
|
||||
if part.inline_data:
|
||||
image_data = part.inline_data.data
|
||||
image = Image.open(BytesIO(image_data))
|
||||
image.save("test_2_0_flash.png")
|
||||
print("✓ Success! Saved test_2_0_flash.png")
|
||||
else:
|
||||
print(f"✗ Model returned text: {part.text[:100]}")
|
||||
else:
|
||||
print("✗ No content parts in response")
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error: {type(e).__name__}: {str(e)[:300]}")
|
||||
194
frontend/public/plugin-logos/generate_with_gemini.py
Normal file
@@ -0,0 +1,194 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import base64
|
||||
from pathlib import Path
|
||||
|
||||
# Gemini API configuration
|
||||
API_KEY = "AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw"
|
||||
API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent"
|
||||
|
||||
OUTPUT_DIR = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos"
|
||||
|
||||
# Plugin configurations
|
||||
plugins = [
|
||||
{
|
||||
'filename': 'daily-appointment-summary.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Daily Appointment Summary Email".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Indigo/blue (#4f46e5)
|
||||
- Icon should combine email and calendar/scheduling concepts
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Professional and trustworthy appearance
|
||||
|
||||
The icon represents a plugin that sends daily email summaries of appointments to staff members.'''
|
||||
},
|
||||
{
|
||||
'filename': 'no-show-tracker.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "No-Show Customer Tracker".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Red (#dc2626)
|
||||
- Icon should represent missed appointments or absent customers
|
||||
- Could show a person silhouette with an X, slash, or cancel symbol
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Professional appearance
|
||||
|
||||
The icon represents a plugin that tracks customers who miss their appointments.'''
|
||||
},
|
||||
{
|
||||
'filename': 'birthday-greetings.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Birthday Greeting Campaign".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Pink (#ec4899)
|
||||
- Icon should represent birthdays and celebrations
|
||||
- Could show a birthday cake, gift, party hat, or balloon
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Friendly and celebratory appearance
|
||||
|
||||
The icon represents a plugin that sends birthday emails with special offers to customers.'''
|
||||
},
|
||||
{
|
||||
'filename': 'monthly-revenue-report.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Monthly Revenue Report".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Green (#10b981)
|
||||
- Icon should represent business growth, analytics, and financial reporting
|
||||
- Could show an upward trending chart, graph, or money symbol
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Professional and successful appearance
|
||||
|
||||
The icon represents a plugin that generates comprehensive monthly business statistics and revenue reports.'''
|
||||
},
|
||||
{
|
||||
'filename': 'appointment-reminder-24hr.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Appointment Reminder (24hr)".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Amber/Orange (#f59e0b)
|
||||
- Icon should represent notifications, alerts, and reminders
|
||||
- Could show a bell with a notification badge, clock, or alarm
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Attention-grabbing but professional appearance
|
||||
|
||||
The icon represents a plugin that sends reminder emails to customers 24 hours before their appointments.'''
|
||||
},
|
||||
{
|
||||
'filename': 'inactive-customer-reengagement.png',
|
||||
'prompt': '''Create a modern, professional icon/logo for a plugin called "Inactive Customer Re-engagement".
|
||||
|
||||
Design requirements:
|
||||
- Square icon, 512x512 pixels
|
||||
- Flat design style with a slight gradient
|
||||
- Primary color: Purple (#8b5cf6)
|
||||
- Icon should represent customer retention, returning customers, or re-engagement
|
||||
- Could show a heart, person with return arrow, refresh symbol, or comeback concept
|
||||
- Clean, minimalist design suitable for a SaaS application
|
||||
- White or light elements on the colored background
|
||||
- Rounded corners (similar to modern app icons)
|
||||
- Warm and welcoming appearance
|
||||
|
||||
The icon represents a plugin that wins back customers who haven't booked appointments recently.'''
|
||||
}
|
||||
]
|
||||
|
||||
def generate_image(prompt, filename):
|
||||
"""Generate an image using Gemini API"""
|
||||
print(f"\nGenerating {filename}...")
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"contents": [{
|
||||
"parts": [{
|
||||
"text": prompt
|
||||
}]
|
||||
}],
|
||||
"generationConfig": {
|
||||
"temperature": 1,
|
||||
"topK": 40,
|
||||
"topP": 0.95,
|
||||
"maxOutputTokens": 8192,
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{API_URL}?key={API_KEY}",
|
||||
headers=headers,
|
||||
json=payload,
|
||||
timeout=120
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
|
||||
# Check if there's an image in the response
|
||||
if 'candidates' in result and len(result['candidates']) > 0:
|
||||
candidate = result['candidates'][0]
|
||||
if 'content' in candidate and 'parts' in candidate['content']:
|
||||
for part in candidate['content']['parts']:
|
||||
if 'inlineData' in part:
|
||||
# Extract and save the image
|
||||
image_data = base64.b64decode(part['inlineData']['data'])
|
||||
output_path = os.path.join(OUTPUT_DIR, filename)
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(image_data)
|
||||
print(f"✓ Saved: {output_path}")
|
||||
return True
|
||||
elif 'text' in part:
|
||||
print(f"Response: {part['text'][:200]}...")
|
||||
|
||||
print(f"✗ No image generated. Response: {result}")
|
||||
return False
|
||||
else:
|
||||
print(f"✗ API Error ({response.status_code}): {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
# Create output directory
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
print("Starting logo generation with Gemini API...")
|
||||
print("=" * 60)
|
||||
|
||||
success_count = 0
|
||||
for plugin in plugins:
|
||||
if generate_image(plugin['prompt'], plugin['filename']):
|
||||
success_count += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Generation complete: {success_count}/{len(plugins)} successful")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
112
frontend/public/plugin-logos/generate_with_gemini_sdk.py
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
import google.generativeai as genai
|
||||
import os
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
# Configure API Key
|
||||
genai.configure(api_key="AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw")
|
||||
|
||||
OUTPUT_DIR = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos"
|
||||
|
||||
# Plugin configurations
|
||||
plugins = [
|
||||
{
|
||||
'filename': 'daily-appointment-summary.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in a square format with rounded corners.
|
||||
Design: Indigo blue gradient background (#4f46e5). White simple envelope icon combined with a small calendar symbol.
|
||||
Style: Flat design, clean geometric shapes, professional SaaS application aesthetic.
|
||||
The icon should be instantly recognizable at 48x48 pixels.'''
|
||||
},
|
||||
{
|
||||
'filename': 'no-show-tracker.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in a square format with rounded corners.
|
||||
Design: Red gradient background (#dc2626). White simple person silhouette with a bold X or cancel symbol overlay.
|
||||
Style: Flat design, clean geometric shapes, professional SaaS application aesthetic.
|
||||
The icon should clearly convey "missed appointment" at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'birthday-greetings.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in a square format with rounded corners.
|
||||
Design: Pink gradient background (#ec4899). White simple birthday cake with candles or gift box with bow.
|
||||
Style: Flat design, clean geometric shapes, cheerful yet professional aesthetic.
|
||||
The icon should feel celebratory and friendly at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'monthly-revenue-report.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in a square format with rounded corners.
|
||||
Design: Green gradient background (#10b981). White simple upward trending line chart or bar graph showing growth.
|
||||
Style: Flat design, clean geometric shapes, professional business analytics aesthetic.
|
||||
The icon should convey success and growth at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'appointment-reminder-24hr.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in a square format with rounded corners.
|
||||
Design: Amber/orange gradient background (#f59e0b). White simple notification bell with a small red alert dot or badge.
|
||||
Style: Flat design, clean geometric shapes, attention-grabbing yet professional aesthetic.
|
||||
The icon should clearly indicate alerts and reminders at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'inactive-customer-reengagement.png',
|
||||
'prompt': '''Create a modern, minimalist app icon in a square format with rounded corners.
|
||||
Design: Purple gradient background (#8b5cf6). White simple heart symbol with a circular refresh/return arrow around it.
|
||||
Style: Flat design, clean geometric shapes, warm and welcoming professional aesthetic.
|
||||
The icon should convey customer care and comeback at small sizes.'''
|
||||
}
|
||||
]
|
||||
|
||||
def generate_image(prompt, filename):
|
||||
"""Generate an image using Gemini 2.0 Flash Image Generation"""
|
||||
print(f"\nGenerating {filename}...")
|
||||
|
||||
try:
|
||||
# Use the image generation model
|
||||
model = genai.GenerativeModel('gemini-2.0-flash-exp-image-generation')
|
||||
|
||||
# Generate the image
|
||||
response = model.generate_content(prompt)
|
||||
|
||||
# Check for generated image
|
||||
if hasattr(response, 'parts') and response.parts:
|
||||
for part in response.parts:
|
||||
if hasattr(part, 'inline_data') and part.inline_data:
|
||||
# Extract image data
|
||||
image_data = part.inline_data.data
|
||||
|
||||
# Save the image
|
||||
output_path = os.path.join(OUTPUT_DIR, filename)
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(image_data)
|
||||
|
||||
print(f"✓ Saved: {output_path} ({len(image_data)} bytes)")
|
||||
return True
|
||||
|
||||
# If no image data found, check if there's text response
|
||||
if hasattr(response, 'text'):
|
||||
print(f"✗ No image generated. Response text: {response.text[:200]}")
|
||||
else:
|
||||
print(f"✗ No image generated. Response: {response}")
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error: {type(e).__name__}: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
# Create output directory
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
print("Starting logo generation with Gemini 2.0 Flash Image Generation...")
|
||||
print("=" * 60)
|
||||
|
||||
success_count = 0
|
||||
for plugin in plugins:
|
||||
if generate_image(plugin['prompt'], plugin['filename']):
|
||||
success_count += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Generation complete: {success_count}/{len(plugins)} successful")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
146
frontend/public/plugin-logos/generate_with_imagen.py
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import base64
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
# Imagen API configuration
|
||||
API_KEY = "AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw"
|
||||
MODEL = "imagen-4.0-generate-preview-06-06"
|
||||
API_URL = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL}:predict"
|
||||
|
||||
OUTPUT_DIR = "/home/poduck/Desktop/smoothschedule2/frontend/public/plugin-logos"
|
||||
|
||||
# Plugin configurations
|
||||
plugins = [
|
||||
{
|
||||
'filename': 'daily-appointment-summary.png',
|
||||
'prompt': '''A modern, minimalist app icon for "Daily Appointment Summary Email" plugin.
|
||||
Square format with rounded corners. Indigo blue gradient background (#4f46e5).
|
||||
White icon showing a combination of an envelope and a calendar.
|
||||
Flat design, clean lines, professional SaaS application style.
|
||||
The icon should be simple and recognizable at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'no-show-tracker.png',
|
||||
'prompt': '''A modern, minimalist app icon for "No-Show Customer Tracker" plugin.
|
||||
Square format with rounded corners. Red gradient background (#dc2626).
|
||||
White icon showing a person silhouette with an X or cancel symbol overlay.
|
||||
Flat design, clean lines, professional SaaS application style.
|
||||
The icon should convey missed appointments clearly at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'birthday-greetings.png',
|
||||
'prompt': '''A modern, minimalist app icon for "Birthday Greeting Campaign" plugin.
|
||||
Square format with rounded corners. Pink gradient background (#ec4899).
|
||||
White icon showing a birthday cake or gift box with a bow.
|
||||
Flat design, clean lines, cheerful yet professional style.
|
||||
The icon should be celebratory and friendly at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'monthly-revenue-report.png',
|
||||
'prompt': '''A modern, minimalist app icon for "Monthly Revenue Report" plugin.
|
||||
Square format with rounded corners. Green gradient background (#10b981).
|
||||
White icon showing an upward trending chart or bar graph.
|
||||
Flat design, clean lines, professional business analytics style.
|
||||
The icon should convey growth and success at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'appointment-reminder-24hr.png',
|
||||
'prompt': '''A modern, minimalist app icon for "Appointment Reminder" plugin.
|
||||
Square format with rounded corners. Amber/orange gradient background (#f59e0b).
|
||||
White icon showing a notification bell with a small red badge or alert dot.
|
||||
Flat design, clean lines, attention-grabbing yet professional style.
|
||||
The icon should convey alerts and reminders clearly at small sizes.'''
|
||||
},
|
||||
{
|
||||
'filename': 'inactive-customer-reengagement.png',
|
||||
'prompt': '''A modern, minimalist app icon for "Inactive Customer Re-engagement" plugin.
|
||||
Square format with rounded corners. Purple gradient background (#8b5cf6).
|
||||
White icon showing a heart with a circular refresh arrow around it.
|
||||
Flat design, clean lines, warm and welcoming professional style.
|
||||
The icon should convey customer care and return at small sizes.'''
|
||||
}
|
||||
]
|
||||
|
||||
def generate_image(prompt, filename):
|
||||
"""Generate an image using Imagen API"""
|
||||
print(f"\nGenerating {filename}...")
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"instances": [{
|
||||
"prompt": prompt
|
||||
}],
|
||||
"parameters": {
|
||||
"sampleCount": 1
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{API_URL}?key={API_KEY}",
|
||||
headers=headers,
|
||||
json=payload,
|
||||
timeout=120
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
|
||||
# Check if there's an image in the predictions
|
||||
if 'predictions' in result and len(result['predictions']) > 0:
|
||||
prediction = result['predictions'][0]
|
||||
if 'bytesBase64Encoded' in prediction:
|
||||
# Extract and save the image
|
||||
image_data = base64.b64decode(prediction['bytesBase64Encoded'])
|
||||
output_path = os.path.join(OUTPUT_DIR, filename)
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(image_data)
|
||||
print(f"✓ Saved: {output_path} ({len(image_data)} bytes)")
|
||||
return True
|
||||
elif 'image' in prediction and 'bytesBase64Encoded' in prediction['image']:
|
||||
image_data = base64.b64decode(prediction['image']['bytesBase64Encoded'])
|
||||
output_path = os.path.join(OUTPUT_DIR, filename)
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(image_data)
|
||||
print(f"✓ Saved: {output_path} ({len(image_data)} bytes)")
|
||||
return True
|
||||
|
||||
print(f"✗ No image generated. Response: {str(result)[:300]}...")
|
||||
return False
|
||||
else:
|
||||
print(f"✗ API Error ({response.status_code}): {response.text[:500]}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error: {str(e)}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
# Create output directory
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
print("Starting logo generation with Imagen 4.0 API...")
|
||||
print("=" * 60)
|
||||
|
||||
success_count = 0
|
||||
for i, plugin in enumerate(plugins):
|
||||
if i > 0:
|
||||
# Add delay between requests to avoid rate limiting
|
||||
print("Waiting 2 seconds before next request...")
|
||||
time.sleep(2)
|
||||
|
||||
if generate_image(plugin['prompt'], plugin['filename']):
|
||||
success_count += 1
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print(f"Generation complete: {success_count}/{len(plugins)} successful")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
frontend/public/plugin-logos/inactive-customer-reengagement.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/public/plugin-logos/monthly-revenue-report.png
Normal file
|
After Width: | Height: | Size: 706 B |
BIN
frontend/public/plugin-logos/no-show-tracker.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
1
frontend/public/plugin-logos/send_email.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-mail"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 332 B |
43
frontend/public/plugin-logos/test_gemini_image.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import google.generativeai as genai
|
||||
import os
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
# Configure API Key
|
||||
genai.configure(api_key="AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw")
|
||||
|
||||
# Try to initialize the model
|
||||
try:
|
||||
generation_model = genai.GenerativeModel('gemini-pro-vision')
|
||||
print("Model initialized successfully")
|
||||
|
||||
# Define prompt
|
||||
prompt = "A modern app icon with a blue background and white envelope symbol"
|
||||
print(f"Generating image for: '{prompt}'...")
|
||||
|
||||
# Try to generate
|
||||
response = generation_model.generate_content(prompt)
|
||||
|
||||
print(f"Response type: {type(response)}")
|
||||
print(f"Response: {response}")
|
||||
|
||||
if hasattr(response, 'parts') and response.parts:
|
||||
print(f"Parts: {response.parts}")
|
||||
if hasattr(response.parts[0], 'inline_data'):
|
||||
print("Has inline_data!")
|
||||
image_data = response.parts[0].inline_data.data
|
||||
image = Image.open(io.BytesIO(image_data))
|
||||
image.save("test_output.png")
|
||||
print("Success! Image saved to test_output.png")
|
||||
else:
|
||||
print("No inline_data attribute")
|
||||
print(f"Part attributes: {dir(response.parts[0])}")
|
||||
else:
|
||||
print("No parts in response or response.parts doesn't exist")
|
||||
if hasattr(response, 'text'):
|
||||
print(f"Response text: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {type(e).__name__}: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
41
frontend/public/plugin-logos/test_gemini_native_image.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from google import genai
|
||||
from google.genai import types
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
import os
|
||||
|
||||
# Setup Client
|
||||
client = genai.Client(api_key="AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw")
|
||||
|
||||
# Define the Prompt
|
||||
prompt = "Create a simple modern app icon with a blue background and white envelope symbol, square format, flat design, minimalist"
|
||||
|
||||
print(f"Asking Gemini to generate: '{prompt}'...")
|
||||
|
||||
# Call the Gemini Model
|
||||
try:
|
||||
response = client.models.generate_content(
|
||||
model='gemini-2.5-flash-image',
|
||||
contents=prompt,
|
||||
config=types.GenerateContentConfig(
|
||||
response_modalities=["IMAGE"] # Tell Gemini to draw, not talk
|
||||
)
|
||||
)
|
||||
|
||||
# Save the Image
|
||||
if response.candidates[0].content.parts:
|
||||
for part in response.candidates[0].content.parts:
|
||||
if part.inline_data:
|
||||
image_data = part.inline_data.data
|
||||
image = Image.open(BytesIO(image_data))
|
||||
image.save("test_gemini_native.png")
|
||||
print("Success! Saved test_gemini_native.png")
|
||||
else:
|
||||
print("Model returned text instead of image:", part.text)
|
||||
else:
|
||||
print("No content parts in response")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
41
frontend/public/plugin-logos/test_google_genai.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from google import genai
|
||||
from google.genai import types
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
import os
|
||||
|
||||
# Initialize the Client
|
||||
client = genai.Client(api_key="AIzaSyB-nR0nkeftKrd42NrNIDcFCj3yFP8JLtw")
|
||||
|
||||
# Define the prompt
|
||||
prompt = "A simple modern app icon with a blue background and white envelope symbol, square format, flat design"
|
||||
|
||||
print(f"Generating image for: '{prompt}'...")
|
||||
|
||||
# Call the API
|
||||
try:
|
||||
response = client.models.generate_images(
|
||||
model='gemini-2.5-flash-image',
|
||||
prompt=prompt,
|
||||
config=types.GenerateImagesConfig(
|
||||
number_of_images=1,
|
||||
aspect_ratio="1:1",
|
||||
safety_filter_level="BLOCK_LOW_AND_ABOVE",
|
||||
person_generation="ALLOW_ADULT"
|
||||
)
|
||||
)
|
||||
|
||||
# Handle the response
|
||||
for i, generated_image in enumerate(response.generated_images):
|
||||
# Convert raw bytes to an image
|
||||
image = Image.open(BytesIO(generated_image.image.image_bytes))
|
||||
|
||||
# Save to disk
|
||||
filename = f"test_generated_image_{i}.png"
|
||||
image.save(filename)
|
||||
print(f"Success! Saved {filename}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error occurred: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
1
frontend/public/plugin-logos/webhook.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
|
||||
|
After Width: | Height: | Size: 349 B |