daily-card · Generador de tarjetas sociales
Resumen en una frase
Introduce título + subtítulo → obtén un PNG que incluye automáticamente la fecha actual, stickers vectoriales relacionados con el tema y una paleta de colores unificada. Renderizado completamente local, control total sobre el texto en chino y resultados reproducibles con los mismos parámetros.
Este repositorio es en sí mismo un directorio de skill: el directorio donde reside este SKILL.md es la raíz del proyecto; el código, las plantillas de configuración y la documentación están en el mismo nivel. Se puede colocar en las rutas de skills de WorkBuddy / Claude Code / Codex CLI, o ser importado por plataformas tipo skillhub desde GitHub.
Instalación y ubicación
Raíz del proyecto = directorio donde se encuentra este SKILL.md. Se puede ejecutar desde cualquier ruta — el código usa __file__ para localizarse, sin depender de rutas absolutas.
cd /ruta/a/daily-card # Reemplazar con la ubicación real de instalación
python3 -m pip install -r requirements.txt
./install.sh # Instalación idempotente: dependencias, inicialización de configuraciones privadas, autoverificación
./scripts/selfcheck.sh # A partir de entonces, se puede ejecutar la autoverificación en cualquier momento (también punto de entrada para CI)
No necesita conexión a internet, ni interfaz de IA, ni API Key. Detección automática de fuentes chinas: macOS usa Hiragino Sans GB / PingFang, Linux usa Noto Sans CJK, Windows usa Microsoft YaHei; si no se encuentra ninguna, se sale con un mensaje indicando que se especifique un archivo de fuente con --font / --font-bold.
El agente debe priorizar el uso de la CLI local, sin necesidad de iniciar ningún servicio. El panel web y la API remota son opcionales (ver «Dos backends opcionales»).
Uso más común: generación por línea de comandos (prioritario para agentes)
python3 cardgen.py "Vive bien hoy" "Haz que los días ordinarios suenen más fuertes"
El comando imprimirá la ruta completa archivo: ...; entrega esta ruta PNG al usuario mediante present_files e incluye en la respuesta el tema y las dimensiones. El directorio de salida por defecto es output/, y el nombre del archivo es <título>-<fecha>-<plataforma>.png.
Parámetros comunes
| Parámetro | Función | Ejemplo |
|---|---|---|
-t / -s | Título / Subtítulo | -t "Buenos días" -s "Primero levántate" |
--platform | Preset de tamaño | xhs(1080×1440) / moments(1080×1080) / wechat(900×500) / story(1080×1920) / wechat2(1080×460) |
--theme | Forzar tema; por defecto se detecta automáticamente por palabras clave | --theme study |
--date | Especificar fecha | --date 2026-01-01 |
--brand | Pie de página | --brand "Claw · Frase del día" |
--out | Ruta de salida específica | --out /tmp/card.png |
--seed | Semilla de composición; misma semilla, misma composición | --seed 42 |
--bg-image | Imagen de fondo personalizada (extracción automática de color) | --bg-image ./bg.jpg |
--bg-scheme | Elegir la enésima paleta de la imagen, o un color primario personalizado | --bg-scheme 2 / --bg-scheme "#C2185B" |
--list-schemes | Mostrar las paletas candidatas de la imagen de fondo en JSON | python3 cardgen.py --bg-image bg.jpg --list-schemes |
--list-themes | Listar todos los temas y stickers | |
--config-dir | Usar otro directorio de configuración | |
--init-config | Reconstruir config/themes.json desde la plantilla de fábrica (sobrescribe temas privados, haz copia de seguridad primero) |
Flujo completo de extracción de color de imagen de fondo
# 1. Ver primero qué paletas puede extraer la imagen (devuelve JSON: name / base / colors / ratio)
python3 cardgen.py --bg-image ./bg.jpg --list-schemes
# 2. Elegir una para generar (0 es el color principal, 1/2/3 son colores secundarios; también se puede dar directamente #RRGGBB)
python3 cardgen.py "El atardecer es la respuesta del día a la noche" "Hoy también has trabajado duro" \
--bg-image ./bg.jpg --bg-scheme 2 --platform xhs
La paleta (fondo, texto, acento, tarjeta, stickers) se deriva completamente del esquema seleccionado, por lo que los colores de los stickers coinciden inevitablemente con la gama cromática de la imagen; el tipo de sticker sigue coincidiendo automáticamente con el tema del título.
Archivos de configuración (modificar configuración, no código)
| Archivo | Contenido | ¿En el repositorio? |
|---|---|---|
config/themes.default.json | Plantilla de fábrica, 11 temas, solo lectura, para «restaurar valores predeterminados» | Sí |
config/stickers.json | Nombres chinos / grupos de 28 stickers + registro de 6 patrones de fondo (con descripción semántica desc) | Sí |
config/themes.json | Configuración activa de temas: label, keywords, 10 colores, motif, stickers, dark | No, datos privados |
config/settings.json | Plataforma predeterminada, pie de página, factor de sobremuestreo, intensidad de granulado, directorio de salida, número de paleta predeterminada | No, datos privados |
themes.json y settings.json pertenecen a datos privados del usuario, no se incluyen en el control de versiones ni se publican con el repositorio público. En la primera ejecución, cardconfig.ensure_config() los copia automáticamente desde la plantilla de fábrica, sin necesidad de crearlos manualmente. Para usar otro directorio de configuración, usa la variable de entorno DAILY_CARD_CONFIG o --config-dir. Ver config/README.md para más detalles.
Temas integrados (11): study morning night coffee sport nature mood work tech festival quote.
Para cambiar colores/stickers: edita directamente colors / stickers / keywords del tema correspondiente en config/themes.json, o usa la API del panel; el script vuelve a leer la configuración en cada ejecución, los cambios surten efecto inmediatamente. Para añadir un tema nuevo, debe incluir las 10 claves de color (#RRGGBB) y al menos un ID de sticker válido. Como es privado, no aparecerá en git status; si necesitas respaldo, copia el archivo manualmente.
Dos backends opcionales
| Backend | Cuándo usarlo | Cómo activarlo |
|---|---|---|
| Ninguno (predeterminado) | Solo para generar tarjetas con CLI | No hacer nada, directamente python3 cardgen.py ... |
| Panel web local (modo independiente) | Para GUI, vista previa en tiempo real, modificación visual de temas | python3 server.py, aloja página y API, interfaz en http://127.0.0.1:8777 |
| Servidor remoto (modo solo API) | Para compartir en equipo, agente necesita endpoint estable | python3 server.py --api-only + proxy inverso nginx con subruta, configurar DAILY_CARD_URL |
Con --api-only, el backend solo responde a /api/* (estáticos servidos por nginx), /api/reveal devuelve 403; GET /api/health muestra version / mode / serves_static / config_dir, útil para verificar la versión en línea.
La CLI siempre se ejecuta en la máquina local del llamante, independientemente del backend. Cuando no hay un servicio iniciado, no llames a /api/*, fallará la conexión.
Panel web local (configuración visual + vista previa en tiempo real)
python3 server.py # http://127.0.0.1:8777 (modo independiente)
python3 server.py --port 9000 --open
python3 server.py --api-only # Despliegue en la nube: solo API, estáticos servidos por nginx
# macOS: también se puede hacer doble clic en start-server.command
Tres paneles: Diseño de tarjetas (formulario + vista previa en tiempo real + descarga/guardado), Configuración de temas (cambiar colores/stickers/palabras clave visualmente y escribir de vuelta a JSON), Configuración global (valores predeterminados y parámetros de renderizado). Las pestañas tienen enlaces profundos #design / #config / #settings.
El panel de configuración de temas tiene tres columnas: lista de temas / editor / vista previa en tiempo real. Al editar colores, patrones de fondo, modo oscuro o stickers, la derecha muestra inmediatamente el borrador sin guardar, incluyendo la tarjeta real, el diagrama semántico de colores (etiquetando dónde se usa cada uno de los 10 colores y cómo se ve el patrón actual), los stickers bajo esa paleta, y el diagnóstico de contraste WCAG / coherencia de luminosidad. La vista previa es solo en memoria, no escribe config/themes.json; solo cuando estés satisfecho, haz clic en «Guardar configuración» para escribir en disco.
Despliegue en subruta del mismo dominio (servidor en la nube)
Todas las URL del frontend se derivan del BASE en tiempo de ejecución (document.baseURI) en web/app.js, el código fuente no tiene rutas raíz de dominio fijas; index.html también usa rutas relativas para style.css / app.js. Si es necesario apuntar la API a un dominio independiente, establece window.DAILY_CARD_BASE antes de cargar app.js. nginx se encarga de eliminar el prefijo /daily-card/api/* a /api/*, no es necesario modificar las rutas del backend.
| Comando | Función |
|---|---|
./scripts/publish-web.sh | Publicar web/ en un directorio versionado y cambiar atómicamente el enlace simbólico current |
./scripts/publish-web.sh --list | Ver versiones publicadas y la actual |
./scripts/publish-web.sh --rollback [ver] | Reversión instantánea, sin necesidad de reiniciar Python |
./scripts/test_subpath.py | Simular en proceso la eliminación del prefijo por nginx, 10 aserciones encadenadas |
La versión estática del frontend se puede publicar y revertir independientemente del backend. Se puede sobrescribir con DAILY_CARD_WEB_ROOT (por defecto /opt/daily-card-web) y DAILY_CARD_WEB_KEEP (por defecto 5). La barra inclinada final es un requisito estricto: /daily-card debe redirigir 301 a /daily-card/, de lo contrario las rutas relativas se resolverían en la raíz del dominio. Consulta la configuración completa de nginx, systemd, caché y autenticación en docs/DEPLOY.md.
Cuando el agente llame a interfaces remotas, DAILY_CARD_URL debe establecerse hasta el prefijo, por ejemplo https://host/daily-card; el código lo concatenará como https://host/daily-card/api/save; no lo escribas como dominio desnudo.
Interfaces JSON (el agente puede llamar directamente, sin navegador)
| Interfaz | Descripción |
|---|---|
GET /api/health | Health check: versión, modo de operación, directorio de configuración (sin necesidad de iniciar el frontend) |
GET /api/bootstrap | Obtener de una vez themes_doc / settings / registry / platforms / rutas |
POST /api/render | Generar vista previa, devuelve {png: dataURL, path, theme, size, bytes} |
POST /api/save | Generar y escribir en output/, devuelve la ruta en disco |
POST /api/theme-preview | Borrador de tema no guardado → tarjeta + diagrama semántico + tira de stickers + diagnóstico de legibilidad, sin escribir archivos de configuración |
POST /api/schemes | Imagen de fondo → múltiples paletas. Enviar {"path":"/abs/bg.jpg"} o {"image":"data:image/png;base64,..."} |
GET /api/schemes?image_id=xxx | Recuperar las paletas de una imagen ya subida |
POST /api/config/themes | {"themes_doc": {...}} Guardar configuración de temas (con validación) |
POST /api/config/settings | {"settings": {...}} Guardar configuración global |
POST /api/config/reset | Restaurar temas de fábrica (sobrescribe temas privados, haz copia de seguridad primero) |
POST /api/reveal | Abrir la carpeta de salida |
GET /api/sticker?id=book&theme=study | PNG del icono de sticker |
Cuerpo de la solicitud para /api/render y /api/save:
{
"title": "Vive bien hoy",
"subtitle": "Haz que los días ordinarios suenen más fuertes",
"platform": "xhs",
"theme": "auto",
"date": null,
"brand": "Claw · Frase del día",
"seed": null,
"bg_image_id": null,
"bg_path": "./bg.jpg",
"bg_scheme": 0,
"dark_override": null
}
bg_schemepuede ser un número de índice o#RRGGBB;dark_overridecomotrue/falsefuerza tarjeta clara/oscura,nullsigue la detección de la imagen.bg_image_ides devuelto por/api/schemes; también se puede darbg_pathcon una ruta accesible por el servidor.- El
pngdevuelto esdata:image/png;base64,..., decodificar y escribir en archivo.
Ejemplo (cuando DAILY_CARD_URL no está establecido, usa local):
DC_URL="${DAILY_CARD_URL:-http://127.0.0.1:8777}"
curl -s "$DC_URL/api/save" \
-H 'Content-Type: application/json' \
-d '{"title":"Vive bien hoy","subtitle":"Haz que los días suenen más fuertes","platform":"xhs"}'
| python3 -c 'import json,sys; print(json.load(sys.stdin)["path"])'
Vista previa de borrador de tema / Diagnóstico de paleta (inspeccionar antes de guardar)
Para evaluar si una paleta es bonita y el texto legible antes de escribir la configuración, llama a /api/theme-preview. theme es el borrador completo del tema, el servidor solo lo mantiene en memoria:
{
"theme": {
"label": "MI TEMA · Nuevo tema",
"keywords": ["check-in"],
"motif": "dots",
"dark": false,
"colors": {
"bg1": "#F7E9C8", "bg2": "#EED6A4", "card": "#FFFCF6",
"ink": "#2E3A2C", "sub": "#6D7570", "accent": "#E0703C",
"accent2": "#5D8B6A", "soft": "#F1D9A8", "pill": "#2E3A2C",
"pill_text": "#FFFDF5"
},
"stickers": ["book", "pencil", "sun"]
},
"title": "Título de ejemplo",
"subtitle": "Subtítulo de ejemplo",
"platform": "moments",
"date": "2026-11-11",
"seed": 7
}
Campos devueltos (card / legend / stickers / sticker_sheet son data:image/png;base64,...):
| Campo | Significado |
|---|---|
card | Tarjeta renderizada con este borrador |
legend | Diagrama semántico de paleta: patrón real + jerarquía de tarjeta + qué gestiona cada uno de los 10 colores |
stickers | Aspecto de los stickers seleccionados bajo la paleta actual |
sticker_sheet | Sprite completo de stickers, junto con sheet (cols/rows/cell/order) para recortar y mostrar |
diagnostics | [{level: ok/warn/tip, text: explicación en chino}] |
contrast | Valores de contraste (ink_card / sub_card / pill_pair / card_bg / accent_bg / bg_light) |
motif / dark / draft_only | Nombre chino del patrón y descripción, indicador claro/oscuro, siempre true para indicar que no está en disco |
diagnostics verifica el contraste según WCAG: legibilidad del texto principal/secundario/etiquetas, diferenciación entre tarjeta y fondo, si el color de acento resalta lo suficiente, y contradicciones como «fondo muy oscuro pero configurado como tema claro». Práctica recomendada: primero POST del borrador para ver el diagnóstico → si hay warn, ajustar colores según las sugerencias y volver a ejecutar → solo cuando no haya warn, llamar a /api/config/themes para guardar.
Sin iniciar el panel, se puede llamar directamente al módulo:
import base64, cardconfig as cfg, cardpreview as cp
draft = cfg.load_doc()["themes"]["study"] # Estructura anidada, incluye colors
draft["colors"]["accent"] = "#C2185B" # Modificar para probar
r = cp.build_preview({"theme": draft, "title": "Ejemplo", "subtitle": "Subtítulo"})
for it in r["diagnostics"]:
print(it["level"], it["text"])
open("/tmp/draft-card.png", "wb").write(base64.b64decode(r["card"].split(",", 1)[1]))
Actualización y sincronización multi-dispositivo
La única fuente de verdad de versiones es el repositorio git; el resto son derivaciones, solo flujo unidireccional:
| Extremo | Método de actualización | Configuración privada |
|---|---|---|
| Local / Servidor en la nube | git pull && ./install.sh | No se ve afectada, no se sobrescribe |
| Versión estática del frontend en servidor en la nube | ./scripts/publish-web.sh, --rollback permite reversión instantánea | Desacoplado del servicio Python, no requiere reinicio |
| Registro de skill local | ./install.sh --link-skill crea enlace simbólico para evitar desviaciones de copia de archivos | No se ve afectada |
| Instantánea en plataformas skillhub | El mantenedor etiqueta y reimporta, naturalmente hay desfase de versión | La plataforma solo obtiene la plantilla de fábrica |
La plantilla de fábrica themes.default.json se actualiza con las versiones; el themes.json privado del usuario nunca se sobrescribe. Para fusionar nuevos temas de fábrica en tu configuración, copia manualmente las entradas correspondientes de themes.default.json, o usa «Exportar JSON → Fusionar → Importar» en el panel; no ejecutes directamente --init-config / /api/config/reset.
Para instrucciones de despliegue en servidor e integración CI/CD, consulta docs/DEPLOY.md. scripts/selfcheck.sh tiene 8 pasos, incluyendo verificación de portabilidad de rutas del frontend y simulación de despliegue en subruta (proxy en proceso, sin necesidad de instalar nginx).
Flujo de trabajo recomendado para el agente
- Extraer título y subtítulo de las palabras del usuario (si no hay subtítulo, añadir una frase corta que se ajuste al tono, o dejarlo vacío).
- Determinar la plataforma: si no se especifica, usar
xhs; si se menciona Cuenta Oficial, usarwechat; si son Momentos, usarmoments. - Si el usuario proporciona una imagen de fondo: primero
--list-schemeso/api/schemespara ver las candidatas, luego seleccionar y generar; si el usuario no especifica, usar la opción 0. - Ejecutar la CLI (sin servicio); solo llamar a
/api/savesi el usuario solicita explícitamente usar el panel/remoto. - Usar present_files para mostrar la imagen final, explicando en la respuesta el tema, dimensiones, fecha y paleta de colores.
- Si el usuario quiere cambiar el estilo → modificar
config/themes.jsono el panel de configuración, no modificarcardgen.py.
Notas importantes
- División del código:
cardgen.pysolo se encarga del renderizado,cardconfig.pyde la carga/guardado/extracción de color,cardpreview.pyde la vista previa de borradores y diagnóstico de paletas,server.pyes el panel; todos leen los mismosconfig/*.json. output/,uploads/,config/themes.json,config/settings.json,.envestán ignorados por.gitignore;examples/son imágenes demo (unos 6.7 MB, no participan en la ejecución, se pueden eliminar).- Los IDs de temas solo permiten letras, números,
-y_; los colores deben ser#RRGGBB; los IDs de stickers deben existir enconfig/stickers.json. - Cuanto mayor sea el tamaño de la imagen y el sobremuestreo, más lento; el sobremuestreo 2x por defecto ya es suficientemente nítido.
server.pyno tiene autenticación y por defecto solo escucha en127.0.0.1. Antes del despliegue público, añade autenticación de proxy inverso o lista blanca de IPs; en la nube, usa--api-onlypara exponer solo la API (verdocs/DEPLOY.md).- En el código fuente no deben aparecer rutas absolutas como
src="/..."/href="/..."/"/api/...";selfcheck.shbloqueará estas regresiones; todas las solicitudes deben pasar porurl().
Limitaciones conocidas / Plan para la próxima versión
- Los stickers no se pueden subir personalizadamente (versión actual): los stickers solo se pueden seleccionar de los 28 stickers vectoriales registrados en
config/stickers.json; no hay un módulo de gestión de biblioteca de stickers independiente, ni se admite la subida de PNG/SVG como stickers. Para añadir formas, el desarrollador debe agregar funciones de dibujos_xxxencardgen.pyy registrarlas enstickers.json. - Plan para la próxima versión: subida de stickers personalizados (PNG/SVG, extracción automática de color para adaptarse al tema), panel de gestión de biblioteca de stickers (añadir/eliminar/agrupar/vista previa/reutilizar), desacoplamiento de stickers y temas. El usuario ha confirmado que esta funcionalidad se implementará en la próxima versión, no en esta.
- La vista previa de borradores de temas usa sobremuestreo 1x para acelerar la respuesta; puede diferir ligeramente en detalles muy finos con respecto a la salida final 2x, pero el diseño y la paleta son consistentes.