Créateur de GIF Slack
Présentation pas à pas des compétences d'agent pour générer des packs GIF Slack thématiques, gérer les invites, les pipelines de cadres et les listes de contrôle de révision.
Source: Contenu adapté de anthropics/skills (MIT).
Une boîte à outils fournissant des utilitaires et des connaissances pour créer des GIF animés optimisés pour Slack.
Exigences de Slack
Dimensions:
- GIF Emoji: 128 x 128 (recommandé)
- GIF de messages: 480 x 480
Paramètres:
- FPS: 10-30 (la taille inférieure correspond à une taille de fichier plus petite)
- Couleurs: 48-128 (moins = taille de fichier plus petite)
- Durée: gardez moins de 3 secondes pour les GIF emoji
Flux de travail de base
from core.gif_builder import GIFBuilder
from PIL import Image, ImageDraw
# 1. Create builder
builder = GIFBuilder(width=128, height=128, fps=10)
# 2. Generate frames
for i in range(12):
frame = Image.new('RGB', (128, 128), (240, 248, 255))
draw = ImageDraw.Draw(frame)
# Draw your animation using PIL primitives
# (circles, polygons, lines, etc.)
builder.add_frame(frame)
# 3. Save with optimization
builder.save('output.gif', num_colors=48, optimize_for_emoji=True)Dessiner des graphiques
Travailler avec des images téléchargées par l'utilisateur
Si un utilisateur télécharge une image, déterminez s'il souhaite:
- Utilisez-le directement (par exemple, "animez ceci", "divisez ceci en images")
- Utilisez-le comme source d'inspiration (par exemple, "créez quelque chose comme ça")
Chargez et travaillez avec des images à l'aide de PIL:
from PIL import Image
uploaded = Image.open('file.png')
# Use directly, or just as reference for colors/styleDessiner à partir de zéro
Lorsque vous dessinez des graphiques à partir de zéro, utilisez les primitives PIL ImageDraw:
from PIL import ImageDraw
draw = ImageDraw.Draw(frame)
# Circles/ovals
draw.ellipse([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)
# Stars, triangles, any polygon
points = [(x1, y1), (x2, y2), (x3, y3), ...]
draw.polygon(points, fill=(r, g, b), outline=(r, g, b), width=3)
# Lines
draw.line([(x1, y1), (x2, y2)], fill=(r, g, b), width=5)
# Rectangles
draw.rectangle([x1, y1, x2, y2], fill=(r, g, b), outline=(r, g, b), width=3)N'utilisez pas: les polices Emoji (peu fiables sur toutes les plates-formes) et ne supposez pas que des graphiques préemballés existent dans cette compétence.
Rendre les graphiques beaux
Les graphiques doivent être soignés et créatifs, et non basiques. Voici comment procéder:
Utilisez des lignes plus épaisses - Définissez toujourswidth=2ou une valeur supérieure pour les contours et les lignes. Les lignes fines (largeur = 1) semblent saccadées et amateures.
Ajouter de la profondeur visuelle:
- Utiliser des dégradés pour les arrière-plans (
create_gradient_background) - Superposez plusieurs formes pour plus de complexité (par exemple, une étoile avec une étoile plus petite à l'intérieur)
Rendre les formes plus intéressantes:
- Ne vous contentez pas de dessiner un simple cercle: ajoutez des reflets, des anneaux ou des motifs.
- Les étoiles peuvent avoir des lueurs (dessinez des versions plus grandes et semi-transparentes derrière)
- Combinez plusieurs formes (étoiles + paillettes, cercles + anneaux)
Faites attention aux couleurs:
- Utilisez des couleurs vives et complémentaires
- Ajouter du contraste (contours sombres sur les formes claires, contours clairs sur les formes sombres)
- Considérez la composition globale
Pour les formes complexes (cœurs, flocons de neige, etc.):
- Utiliser des combinaisons de polygones et d'ellipses
- Calculez soigneusement les points pour la symétrie
- Ajoutez des détails (un cœur peut avoir une courbe de surbrillance, les flocons de neige ont des branches complexes)
Soyez créatif et détaillé! Un bon GIF Slack doit avoir un aspect soigné, pas comme des graphiques fictifs.
Utilitaires disponibles
Générateur GIF (core.gif_builder)
Assemble les cadres et optimise pour Slack:
builder = GIFBuilder(width=128, height=128, fps=10)
builder.add_frame(frame) # Add PIL Image
builder.add_frames(frames) # Add list of frames
builder.save('out.gif', num_colors=48, optimize_for_emoji=True, remove_duplicates=True)Validateurs (core.validators)
Vérifiez si GIF répond aux exigences de Slack:
from core.validators import validate_gif, is_slack_ready
# Detailed validation
passes, info = validate_gif('my.gif', is_emoji=True, verbose=True)
# Quick check
if is_slack_ready('my.gif'):
print("Ready!")Fonctions d'assouplissement (core.easing)
Mouvement fluide au lieu de linéaire:
from core.easing import interpolate
# Progress from 0.0 to 1.0
t = i / (num_frames - 1)
# Apply easing
y = interpolate(start=0, end=400, t=t, easing='ease_out')
# Available: linear, ease_in, ease_out, ease_in_out,
# bounce_out, elastic_out, back_outAides au cadre (core.frame_composer)
Fonctions de confort pour les besoins courants:
from core.frame_composer import (
create_blank_frame, # Solid color background
create_gradient_background, # Vertical gradient
draw_circle, # Helper for circles
draw_text, # Simple text rendering
draw_star # 5-pointed star
)Concepts d'animation
Secouer/Vibrer
Position de l'objet décalé avec oscillation:
- Utilisez
math.sin()oumath.cos()avec indice de trame - Ajoutez de petites variations aléatoires pour une sensation naturelle
- Appliquer à la position x et/ou y
Pouls/battement cardiaque
Mettre à l'échelle la taille de l'objet de manière rythmée:
- Utilisez
math.sin(t * frequency * 2 * math.pi)pour une impulsion douce - Pour le rythme cardiaque: deux impulsions rapides puis une pause (ajuster l'onde sinusoïdale)
- Échelle entre 0,8 et 1,2 de la taille de base
Rebondir
L'objet tombe et rebondit:
- Utilisez
interpolate()aveceasing='bounce_out'pour l'atterrissage - Utilisez
easing='ease_in'pour tomber (accélérer) - Appliquer la gravité en augmentant la vitesse y à chaque image
Tourner/Rotation
Faire pivoter l'objet autour du centre:
- PIL:
image.rotate(angle, resample=Image.BICUBIC) - Pour l'oscillation: utilisez une onde sinusoïdale pour l'angle au lieu de linéaire
Fondu entrant/sortant
Apparaissent ou disparaissent progressivement:
- Créer une image RGBA, ajuster le canal alpha
- Ou utilisez
Image.blend(image1, image2, alpha) - Fondu entrant: alpha de 0 à 1
- Fondu sortant: alpha de 1 à 0
Diapositive
Déplacer un objet hors écran vers une position:
- Position de départ: en dehors des limites du cadre
- Position finale: emplacement cible
- Utilisez
interpolate()aveceasing='ease_out'pour un arrêt en douceur - Pour le dépassement: utilisez
easing='back_out'
Zoomer
Échelle et position pour l'effet zoom:
- Zoom avant: échelle de 0,1 à 2,0, centre de recadrage
- Zoom arrière: échelle de 2,0 à 1,0
- Peut ajouter un flou de mouvement pour le drame (filtre PIL)
Explosion/Explosion de particules
Créez des particules rayonnant vers l’extérieur:
- Générez des particules avec des angles et des vitesses aléatoires
- Mettez à jour chaque particule:
x += vx,y += vy - Ajouter de la gravité:
vy += gravity_constant - Estompe les particules au fil du temps (réduit l'alpha)
Stratégies d'optimisation
Uniquement lorsqu'on vous demande de réduire la taille du fichier, implémentez quelques-unes des méthodes suivantes:
- Moins d'images - FPS inférieur (10 au lieu de 20) ou durée plus courte
- Moins de couleurs -
num_colors=48au lieu de 128 - Dimensions plus petites - 128x128 au lieu de 480x480
- Supprimer les doublons -
remove_duplicates=Truedans save() - Mode Emoji -
optimize_for_emoji=Trues'optimise automatiquement
# Maximum optimization for emoji
builder.save(
'emoji.gif',
num_colors=48,
optimize_for_emoji=True,
remove_duplicates=True
)Philosophie
Cette compétence permet:
- Connaissances: exigences de Slack et concepts d'animation
- Utilitaires: GIFBuilder, validateurs, fonctions d'assouplissement
- Flexibilité: Créez la logique d'animation à l'aide des primitives PIL
Il ne fournit PAS:
- Modèles d'animation rigides ou fonctions prédéfinies
- Rendu des polices Emoji (peu fiable sur toutes les plateformes)
- Une bibliothèque de graphiques préemballés intégrée à la compétence
Remarque sur les téléchargements d'utilisateurs: Cette compétence n'inclut pas de graphiques prédéfinis, mais si un utilisateur télécharge une image, utilisez PIL pour la charger et l'utiliser - interprétez en fonction de sa demande s'il souhaite qu'elle soit utilisée directement ou simplement comme source d'inspiration.
Soyez créatif! Combinez les concepts (rebond + rotation, pulsation + glissement, etc.) et utilisez toutes les capacités de PIL.
Dépendances
pip install pillow imageio numpyFichiers de ressources
LICENCE.txt
Ressource binaire
core/easing.py
Ressource binaire
core/frame_composer.py
Télécharger core/frame_composer.py
#!/usr/bin/env python3
"""
Frame Composer - Utilities for composing visual elements into frames.
Provides functions for drawing shapes, text, emojis, and compositing elements
together to create animation frames.
"""
from typing import Optional
import numpy as np
from PIL import Image, ImageDraw, ImageFont
def create_blank_frame(
width: int, height: int, color: tuple[int, int, int] = (255, 255, 255)
) -> Image.Image:
"""
Create a blank frame with solid color background.
Args:
width: Frame width
height: Frame height
color: RGB color tuple (default: white)
Returns:
PIL Image
"""
return Image.new("RGB", (width, height), color)
def draw_circle(
frame: Image.Image,
center: tuple[int, int],
radius: int,
fill_color: Optional[tuple[int, int, int]] = None,
outline_color: Optional[tuple[int, int, int]] = None,
outline_width: int = 1,
) -> Image.Image:
"""
Draw a circle on a frame.
Args:
frame: PIL Image to draw on
center: (x, y) center position
radius: Circle radius
fill_color: RGB fill color (None for no fill)
outline_color: RGB outline color (None for no outline)
outline_width: Outline width in pixels
Returns:
Modified frame
"""
draw = ImageDraw.Draw(frame)
x, y = center
bbox = [x - radius, y - radius, x + radius, y + radius]
draw.ellipse(bbox, fill=fill_color, outline=outline_color, width=outline_width)
return frame
def draw_text(
frame: Image.Image,
text: str,
position: tuple[int, int],
color: tuple[int, int, int] = (0, 0, 0),
centered: bool = False,
) -> Image.Image:
"""
Draw text on a frame.
Args:
frame: PIL Image to draw on
text: Text to draw
position: (x, y) position (top-left unless centered=True)
color: RGB text color
centered: If True, center text at position
Returns:
Modified frame
"""
draw = ImageDraw.Draw(frame)
# Uses Pillow's default font.
# If the font should be changed for the emoji, add additional logic here.
font = ImageFont.load_default()
if centered:
bbox = draw.textbbox((0, 0), text, font=font)
text_width = bbox[2] - bbox[0]
text_height = bbox[3] - bbox[1]
x = position[0] - text_width // 2
y = position[1] - text_height // 2
position = (x, y)
draw.text(position, text, fill=color, font=font)
return frame
def create_gradient_background(
width: int,
height: int,
top_color: tuple[int, int, int],
bottom_color: tuple[int, int, int],
) -> Image.Image:
"""
Create a vertical gradient background.
Args:
width: Frame width
height: Frame height
top_color: RGB color at top
bottom_color: RGB color at bottom
Returns:
PIL Image with gradient
"""
frame = Image.new("RGB", (width, height))
draw = ImageDraw.Draw(frame)
# Calculate color step for each row
r1, g1, b1 = top_color
r2, g2, b2 = bottom_color
for y in range(height):
# Interpolate color
ratio = y / height
r = int(r1 * (1 - ratio) + r2 * ratio)
g = int(g1 * (1 - ratio) + g2 * ratio)
b = int(b1 * (1 - ratio) + b2 * ratio)
# Draw horizontal line
draw.line([(0, y), (width, y)], fill=(r, g, b))
return frame
def draw_star(
frame: Image.Image,
center: tuple[int, int],
size: int,
fill_color: tuple[int, int, int],
outline_color: Optional[tuple[int, int, int]] = None,
outline_width: int = 1,
) -> Image.Image:
"""
Draw a 5-pointed star.
Args:
frame: PIL Image to draw on
center: (x, y) center position
size: Star size (outer radius)
fill_color: RGB fill color
outline_color: RGB outline color (None for no outline)
outline_width: Outline width
Returns:
Modified frame
"""
import math
draw = ImageDraw.Draw(frame)
x, y = center
# Calculate star points
points = []
for i in range(10):
angle = (i * 36 - 90) * math.pi / 180 # 36 degrees per point, start at top
radius = size if i % 2 == 0 else size * 0.4 # Alternate between outer and inner
px = x + radius * math.cos(angle)
py = y + radius * math.sin(angle)
points.append((px, py))
# Draw star
draw.polygon(points, fill=fill_color, outline=outline_color, width=outline_width)
return framecore/gif_builder.py
Télécharger core/gif_builder.py
Ressource binaire
core/validateurs.py
Télécharger core/validators.py
#!/usr/bin/env python3
"""
Validators - Check if GIFs meet Slack's requirements.
These validators help ensure your GIFs meet Slack's size and dimension constraints.
"""
from pathlib import Path
def validate_gif(
gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
) -> tuple[bool, dict]:
"""
Validate GIF for Slack (dimensions, size, frame count).
Args:
gif_path: Path to GIF file
is_emoji: True for emoji (128x128 recommended), False for message GIF
verbose: Print validation details
Returns:
Tuple of (passes: bool, results: dict with all details)
"""
from PIL import Image
gif_path = Path(gif_path)
if not gif_path.exists():
return False, {"error": f"File not found: {gif_path}"}
# Get file size
size_bytes = gif_path.stat().st_size
size_kb = size_bytes / 1024
size_mb = size_kb / 1024
# Get dimensions and frame info
try:
with Image.open(gif_path) as img:
width, height = img.size
# Count frames
frame_count = 0
try:
while True:
img.seek(frame_count)
frame_count += 1
except EOFError:
pass
# Get duration
try:
duration_ms = img.info.get("duration", 100)
total_duration = (duration_ms * frame_count) / 1000
fps = frame_count / total_duration if total_duration > 0 else 0
except:
total_duration = None
fps = None
except Exception as e:
return False, {"error": f"Failed to read GIF: {e}"}
# Validate dimensions
if is_emoji:
optimal = width == height == 128
acceptable = width == height and 64 <= width <= 128
dim_pass = acceptable
else:
aspect_ratio = (
max(width, height) / min(width, height)
if min(width, height) > 0
else float("inf")
)
dim_pass = aspect_ratio <= 2.0 and 320 <= min(width, height) <= 640
results = {
"file": str(gif_path),
"passes": dim_pass,
"width": width,
"height": height,
"size_kb": size_kb,
"size_mb": size_mb,
"frame_count": frame_count,
"duration_seconds": total_duration,
"fps": fps,
"is_emoji": is_emoji,
"optimal": optimal if is_emoji else None,
}
# Print if verbose
if verbose:
print(f"\nValidating {gif_path.name}:")
print(
f" Dimensions: {width}x{height}"
+ (
f" ({'optimal' if optimal else 'acceptable'})"
if is_emoji and acceptable
else ""
)
)
print(
f" Size: {size_kb:.1f} KB"
+ (f" ({size_mb:.2f} MB)" if size_mb >= 1.0 else "")
)
print(
f" Frames: {frame_count}"
+ (f" @ {fps:.1f} fps ({total_duration:.1f}s)" if fps else "")
)
if not dim_pass:
print(
f" Note: {'Emoji should be 128x128' if is_emoji else 'Unusual dimensions for Slack'}"
)
if size_mb > 5.0:
print(f" Note: Large file size - consider fewer frames/colors")
return dim_pass, results
def is_slack_ready(
gif_path: str | Path, is_emoji: bool = True, verbose: bool = True
) -> bool:
"""
Quick check if GIF is ready for Slack.
Args:
gif_path: Path to GIF file
is_emoji: True for emoji GIF, False for message GIF
verbose: Print feedback
Returns:
True if dimensions are acceptable
"""
passes, _ = validate_gif(gif_path, is_emoji, verbose)
return passesexigences.txt
pillow>=10.0.0
imageio>=2.31.0
imageio-ffmpeg>=0.4.9
numpy>=1.24.0Voir dans GitHub
Conception frontale
Créez des interfaces frontales distinctives de qualité production avec une qualité de conception élevée. Utilisez cette compétence lorsque l'utilisateur demande à créer des composants Web, des pages, des artefacts, des affiches ou des applications (les exemples incluent des sites Web, des pages de destination, des tableaux de bord, des composants React, des mises en page HTML/CSS ou lors du style/embellissement d'une interface utilisateur Web). Génère un code créatif et raffiné et une conception d'interface utilisateur qui évite l'esthétique générique de l'IA.
Générateur d'artefacts Web
Compétence d'agent pour regrouper les artefacts React et Tailwind dans des sorties HTML uniques avec des composants shadcn et des scripts d'automatisation.
claudeskills Docs