Creador de gifs flojos
Tutorial de habilidades del agente para generar paquetes GIF temáticos de Slack, administrar mensajes, canalizaciones de marcos y listas de verificación de revisión.
Fuente: Contenido adaptado de anthropics/skills (MIT).
Un conjunto de herramientas que proporciona utilidades y conocimientos para crear GIF animados optimizados para Slack.
Requisitos de holgura
Dimensiones:
- GIF de emojis: 128x128 (recomendado)
- GIF de mensajes: 480x480
Parámetros:
- FPS: 10-30 (menor es el tamaño de archivo más pequeño)
- Colores: 48-128 (menos = tamaño de archivo más pequeño)
- Duración: Mantenga menos de 3 segundos para GIF de emoji
Flujo de trabajo principal
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)Dibujo de gráficos
Trabajar con imágenes cargadas por usuarios
Si un usuario sube una imagen, considere si quiere:
- Úselo directamente (por ejemplo, "animar esto", "dividir esto en cuadros")
- Úsalo como inspiración (por ejemplo, "haz algo como esto")
Cargue y trabaje con imágenes usando PIL:
from PIL import Image
uploaded = Image.open('file.png')
# Use directly, or just as reference for colors/styleDibujar desde cero
Al dibujar gráficos desde cero, utilice las primitivas 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)No utilices: fuentes de emoji (no confiables en todas las plataformas) ni asumas que existen gráficos preempaquetados en esta habilidad.
Hacer que los gráficos se vean bien
Los gráficos deben verse pulidos y creativos, no básicos. He aquí cómo:
Utilice líneas más gruesas: establezca siemprewidth=2o superior para contornos y líneas. Las líneas finas (ancho = 1) parecen entrecortadas y de aficionado.
Agregar profundidad visual:
- Utilice degradados para fondos (
create_gradient_background) - Coloque capas de varias formas para lograr complejidad (por ejemplo, una estrella con una estrella más pequeña en su interior)
Haz que las formas sean más interesantes:
- No se limite a dibujar un círculo simple: agregue reflejos, anillos o patrones
- Las estrellas pueden tener brillos (dibuja versiones más grandes y semitransparentes detrás)
- Combina múltiples formas (estrellas + destellos, círculos + anillos)
Presta atención a los colores:
- Utilice colores vibrantes y complementarios
- Agregue contraste (contornos oscuros sobre formas claras, contornos claros sobre formas oscuras)
- Considere la composición general.
Para formas complejas (corazones, copos de nieve, etc.):
- Utilice combinaciones de polígonos y elipses.
- Calcular puntos cuidadosamente para la simetría.
- Agregue detalles (un corazón puede tener una curva resaltada, los copos de nieve tienen ramas intrincadas)
¡Sea creativo y detallista! Un buen GIF de Slack debe verse pulido, no como un gráfico de marcador de posición.
Utilidades disponibles
Generador de GIF (core.gif_builder)
Ensambla marcos y optimiza para 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)Validadores (core.validators)
Comprueba si el GIF cumple con los requisitos 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!")Funciones de relajación (core.easing)
Movimiento suave en lugar de lineal:
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_outAyudantes de marco (core.frame_composer)
Funciones de conveniencia para necesidades comunes:
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
)Conceptos de animación
Agitar/Vibrar
Posición del objeto desplazado con oscilación:
- Utilice
math.sin()omath.cos()con índice de cuadros - Agregue pequeñas variaciones aleatorias para una sensación natural
- Aplicar a la posición x y/o y
Pulso/latido del corazón
Escale el tamaño del objeto rítmicamente:
- Utilice
math.sin(t * frequency * 2 * math.pi)para un pulso suave - Para latidos del corazón: dos pulsos rápidos y luego una pausa (ajustar la onda sinusoidal)
- Escala entre 0,8 y 1,2 del tamaño base.
Rebotar
El objeto cae y rebota:
- Utilice
interpolate()coneasing='bounce_out'para aterrizar - Utilice
easing='ease_in'para caer (acelerar) - Aplicar la gravedad aumentando la velocidad y en cada cuadro.
Girar/Rotar
Girar objeto alrededor del centro:
- PIL:
image.rotate(angle, resample=Image.BICUBIC) - Para oscilación: use onda sinusoidal para ángulo en lugar de lineal
Fundido de entrada/salida
Aparecen o desaparecen gradualmente:
- Crear imagen RGBA, ajustar el canal alfa
- O utilice
Image.blend(image1, image2, alpha) - Fundido de entrada: alfa de 0 a 1
- Desvanecimiento: alfa de 1 a 0
Deslizar
Mover el objeto desde fuera de la pantalla a la posición:
- Posición inicial: fuera de los límites del marco
- Posición final: ubicación de destino
- Utilice
interpolate()coneasing='ease_out'para una parada suave - Para sobrepasar: use
easing='back_out'
Zoom
Escala y posición para efecto de zoom:
- Ampliar: escala de 0,1 a 2,0, centro de recorte
- Alejar: escalar de 2,0 a 1,0
- Puede agregar desenfoque de movimiento para dramatismo (filtro PIL)
Explosión/Explosión de partículas
Crea partículas que irradian hacia afuera:
- Genera partículas con ángulos y velocidades aleatorias.
- Actualiza cada partícula:
x += vx,y += vy - Añadir gravedad:
vy += gravity_constant - Desaparecen las partículas con el tiempo (reducen alfa)
Estrategias de optimización
Solo cuando se le solicite reducir el tamaño del archivo, implemente algunos de los siguientes métodos:
- Menos cuadros - FPS más bajos (10 en lugar de 20) o duración más corta
- Menos colores -
num_colors=48en lugar de 128 - Dimensiones más pequeñas - 128x128 en lugar de 480x480
- Eliminar duplicados -
remove_duplicates=Trueen guardar() - Modo Emoji -
optimize_for_emoji=Truese optimiza automáticamente
# Maximum optimization for emoji
builder.save(
'emoji.gif',
num_colors=48,
optimize_for_emoji=True,
remove_duplicates=True
)Filosofía
Esta habilidad proporciona:
- Conocimiento: requisitos de Slack y conceptos de animación
- Utilidades: GIFBuilder, validadores, funciones de facilitación
- Flexibilidad: crea la lógica de animación usando primitivas PIL
NO proporciona:
- Plantillas de animación rígidas o funciones prefabricadas.
- Representación de fuentes emoji (poco confiable en todas las plataformas)
- Una biblioteca de gráficos preempaquetados integrados en la habilidad.
Nota sobre las cargas de usuarios: esta habilidad no incluye gráficos prediseñados, pero si un usuario carga una imagen, use PIL para cargarla y trabajar con ella; interprete según su solicitud si quiere que se use directamente o simplemente como inspiración.
¡Sea creativo! Combine conceptos (rebote + rotación, pulsación + deslizamiento, etc.) y utilice todas las capacidades de PIL.
Dependencias
pip install pillow imageio numpyArchivos de recursos
LICENCIA.txt
Recurso binario
core/easing.py
Recurso binario
núcleo/frame_composer.py
Descargar 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 framenúcleo/gif_builder.py
Recurso binario
core/validadores.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 passesrequisitos.txt
pillow>=10.0.0
imageio>=2.31.0
imageio-ffmpeg>=0.4.9
numpy>=1.24.0Ver en GitHub
Diseño de interfaz
Cree interfaces frontales distintivas y de calidad de producción con alta calidad de diseño. Utilice esta habilidad cuando el usuario solicite crear componentes web, páginas, artefactos, carteles o aplicaciones (los ejemplos incluyen sitios web, páginas de destino, paneles, componentes de React, diseños HTML/CSS o cuando diseñe o embellezca cualquier interfaz de usuario web). Genera código creativo y pulido y diseño de interfaz de usuario que evita la estética genérica de la IA.
Generador de artefactos web
Habilidad del agente para agrupar artefactos de React y Tailwind en salidas HTML únicas con componentes shadcn y scripts de automatización.
claudeskills Docs