gráfico-chart
Genera gráficos de visualización de datos como PNG. Renderiza HTML con Apache ECharts v6 en Chromium sin interfaz gráfica a través de Playwright → capturas de pantalla con calidad retina 2×.
CDN: https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js
Reglas críticas (leer antes de cada generación)
- Tipo
area→type: 'line'+areaStyle: {}— ECharts no tienetype: 'area'. doughnut→type: 'pie'+radius: ['40%', '70%']— ECharts no tienetype: 'doughnut'.- Señal de listo: registrar
chart.on('finished', fn)ANTES dechart.setOption()— Error de ECharts #14101/#17500: si el listener se registra después de setOption, nunca se dispara silenciosamente. Siempre registrar los eventosfinishedyrenderedantes de setOption. xAxis.type: 'category'debe ser explícito — ECharts no lo infiere de los datos. Olvidar esto produce un gráfico en blanco.- Las etiquetas de categoría van en
xAxis.data, no en un arreglodata.labels. La estructura de ECharts es plana:{ xAxis, yAxis, series, grid, title, legend }— no anidada bajodataooptions. - Las etiquetas de datos son totalmente integradas — usar
label: { show: true }en cualquier serie. No se necesita plugin. - Resaltar una barra/punto específico mediante
itemStylepor elemento — poner{ value: N, itemStyle: { color: '#...' } }directamente en el arreglodata. NO usar arreglosbackgroundColoral estilo Chart.js. - La inicialización de ECharts usa un contenedor
<div>, no<canvas>—echarts.init(document.getElementById('chart')). El div contenedor necesita dimensiones explícitas. animation: falseen la opción — desactiva la animación para renderizado instantáneo. Aún así, registrar los eventosfinished+renderedantes de setOption para la señal de listo.- Nunca volcar HTML en el chat. Guardar en archivo, mostrar solo el resumen.
- El título expresa la conclusión, no el tema. "Los ingresos crecieron 3× en 12 meses" no "Ingresos Mensuales".
- Gráfico circular/dona: usar
padding: 64px 80pxen el cuerpo y.chart-container { max-height: 860px }— evita el relleno de borde a borde cuando no hay título.
Paso 1: Captura de entrada
Requerido: chart_type, data
Parámetros opcionales y valores predeterminados:
| Parámetro | Predeterminado | Descripción |
|---|---|---|
| chart_type | — | bar / line / area / pie / doughnut / scatter / radar / treemap |
| data | — | Arreglo JSON o CSV — requerido |
| title | — | Expresa la conclusión, ≤10 palabras |
| subtitle | — | Línea de contexto de 1 oración |
| style | clean-slate | clean-slate / midnight-editorial / matt-gray / electric-burst / brutalist |
| dimensions | 1080x1080 | WxH píxeles (PNG de salida = 2× mediante deviceScaleFactor) |
| x_label | — | Texto de la etiqueta del eje X |
| y_label | — | Texto de la etiqueta del eje Y |
| source | — | Fuente de datos mostrada en el pie de página |
| highlight | — | Etiqueta de datos a resaltar (ej. "Q4", "Dec", índice 3) |
Si falta chart_type o data, preguntar exactamente:
"Para crear el gráfico, necesito:
- Tipo de gráfico — bar / line / area / pie / doughnut / scatter / radar / treemap
- Datos — proporcionar como arreglo JSON o CSV (ej.
[12, 18, 22, 25, 31]con etiquetas['Q1','Q2','Q3','Q4','Q5'])Opcional: título, estilo (predeterminado: clean-slate), dimensiones (predeterminado: 1080×1080), resaltar un punto de datos específico"
Si ambos están presentes → saltar al Paso 2.
Paso 2: Arquitectura interna (nunca se muestra al usuario)
1. Normalizar el tipo de gráfico:
area→line+areaStyle: {}en la seriedoughnut→pie+radius: ['40%', '70%']en la seriehorizontal bar→bar+ intercambiar xAxis/yAxis (eje de categoría en y)- Todos los demás: usar tal cual
2. Leer references/chart-library.md — cargar la especificación de configuración completa para este tipo de gráfico.
3. Leer references/style-presets.md — cargar tokens CSS + paleta de datos para el estilo elegido.
4. Comprometerse con la dirección de diseño:
| Decisión | Derivar de |
|---|---|
| Tono | Profesional / editorial / audaz / técnico — coincidir con la audiencia de los datos |
| Historia de datos | Conclusión única que este gráfico demuestra (se convierte en el título) |
| Estrategia de resaltado | ¿Qué punto de datos necesita énfasis visual y por qué? |
| Fondo | Claro (clean-slate, matt-gray) u oscuro (midnight-editorial, electric-burst, brutalist) |
5. Analizar datos:
- Arreglo simple
[12, 18, 22]→ series.data, etiquetas proporcionadas por separado - Arreglo de objetos
[{x: 'Jan', y: 12}]→ xAxis.data de las claves x, series.data de los valores y - CSV: analizar la fila del encabezado como xAxis.data, la fila de valores como series.data
- Múltiples series: múltiples entradas
seriescada una contype,name,data - Dispersión:
series.data: [[x1,y1], [x2,y2], ...]formato
6. Analizar dimensiones: "1080x1080" → W=1080, H=1080. Cuerpo = WxH. PNG de salida = 2W × 2H.
Paso 3: Generación de HTML
Leer TODO antes de generar:
references/chart-library.mdpara la especificación completa de configuración de ECharts de este tipo de gráficoreferences/style-presets.mdpara los tokens CSS + paleta del estilo elegido
Estructura HTML requerida:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[enlace CDN de fuente desde el preset de estilo]
<style>
:root {
[todos los tokens CSS del preset de estilo]
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: [W]px; height: [H]px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
}
body {
display: flex;
flex-direction: column;
padding: 40px 48px 32px; /* gráfico circular/dona: usar 64px 80px */
}
/* El contenedor de ECharts debe tener un tamaño explícito */
.chart-container {
flex: 1;
min-height: 0;
/* solo gráfico circular/dona: max-height: 860px; */
}
.chart-header { margin-bottom: 24px; }
.chart-title {
font-family: var(--font-display);
font-size: clamp(1.1rem, 2.5vw, 1.6rem);
font-weight: 700;
color: var(--text);
line-height: 1.2;
}
.chart-subtitle {
font-family: var(--font-body);
font-size: clamp(0.75rem, 1.2vw, 0.9rem);
color: var(--text-muted);
margin-top: 6px;
line-height: 1.5;
}
.chart-footer {
margin-top: 14px;
font-family: var(--font-body);
font-size: 10px;
color: var(--text-muted);
opacity: 0.65;
}
</style>
</head>
<body>
[si hay título o subtítulo: <div class="chart-header"><div class="chart-title">...</div>...</div>]
<div id="chart" class="chart-container"></div>
[si hay source: <div class="chart-footer">Fuente: [source]</div>]
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
<script>
window.__chartReady = false;
document.fonts.ready.then(() => {
const container = document.getElementById('chart');
const chart = echarts.init(container, null, { renderer: 'canvas' });
// CRÍTICO: registrar eventos ANTES de setOption — Error de ECharts #14101/#17500
// 'finished' puede no dispararse silenciosamente si se registra después de setOption con animation:false
chart.on('finished', () => {
window.__chartReady = true;
});
// Respaldo de seguridad mediante 'rendered'
chart.on('rendered', () => {
clearTimeout(window.__renderDebounce);
window.__renderDebounce = setTimeout(() => { window.__chartReady = true; }, 100);
});
const palette = [paleta del preset de estilo];
const option = {
animation: false, // renderizado instantáneo para la captura
backgroundColor: 'transparent', // el CSS del cuerpo maneja el color de fondo
textStyle: {
fontFamily: '[valor de --font-body]',
color: '[valor de --text-muted]',
},
[configuración de título si se proporcionó el parámetro title],
[configuración de leyenda según el tipo de gráfico],
[configuración de cuadrícula según el tipo de gráfico],
[configuración de xAxis según el tipo de gráfico],
[configuración de yAxis según el tipo de gráfico],
series: [{
[configuración completa de la serie desde chart-library.md para este tipo]
[colores de paleta aplicados según el tipo de gráfico]
[si hay highlight: itemStyle por elemento en el punto de datos resaltado]
}]
};
chart.setOption(option); // setOption SIEMPRE va después del registro de eventos
});
</script>
</body>
</html>
Reglas de calidad de diseño:
- Fuente del título:
fontWeight: 'bold',fontSize: 20–24— sin títulos delgados - Líneas de cuadrícula: opacidad baja (0.06–0.10) — subordinadas a los datos
- Barras:
barMaxWidth: 60, redondeadas medianteitemStyle.borderRadius: [4,4,0,0] - Líneas:
smooth: truepara curvas naturales,symbolSize: 8para puntos - Gráfico circular/dona:
label.formatter: '{b}\n{d}%'para etiquetas integradas en las secciones - Presets oscuros: cuadrícula
rgba(255,255,255,0.07), color de línea de eje/marcargba(255,255,255,0.15) - Tooltip:
show: false— PNG estático, sin interacción de hover - Cuando no se proporciona título: omitir
.chart-header, omitir título en la opción de ECharts
Paso 4: Autoverificación (corregir cada fallo antes del Paso 5)
Estructura:
- El contenedor es un
<div>, no<canvas> -
window.__chartReady = falsedeclarado antes dedocument.fonts.ready -
chart.on('finished', ...)registrado ANTES dechart.setOption() -
chart.on('rendered', ...)respaldo con debounce registrado ANTES dechart.setOption() -
animation: falseen el objeto de opción -
chart.setOption(option)es la ÚLTIMA llamada en el bloque de inicialización
Específico del tipo:
- Área:
type: 'line'+areaStyle: {}— notype: 'area' - Dona:
type: 'pie'+radius: ['40%', '70%']— notype: 'doughnut' - Barra:
xAxis.type: 'category'establecido explícitamente - Datos de categoría en
xAxis.data(nodata.labels) - Gráfico circular/dona:
padding: 64px 80pxen el cuerpo +.chart-container { max-height: 860px } - Resaltado:
{ value: N, itemStyle: { color } }por elemento en el arreglo de datos
Diseño:
- Todos los colores de la paleta de
references/style-presets.md - El título expresa la conclusión (no solo el tema)
-
tooltip: { show: false }u omitido (sin hover en PNG estático) - Preset oscuro: colores de cuadrícula/eje usan
rgba(255,255,255,...) - Fuente en el pie de página si se proporciona el parámetro
source - Etiquetas de datos visibles (integrado
label: { show: true }en la serie)
Paso 5: Exportación
Determinar slug a partir del título o tipo de gráfico + contexto de datos (kebab-case, ≤30 caracteres):
mkdir -p chart/[slug]
Guardar HTML: chart/[slug]/chart.html
Verificación rápida en el navegador:
open chart/[slug]/chart.html
Ejecutar exportación (reemplazar [skill-root] con la ruta real al directorio de esta habilidad):
bash [skill-root]/scripts/export-chart.sh \
chart/[slug]/chart.html \
chart/[slug]/chart.png \
--width [W] \
--height [H]
El script instala Playwright en la primera ejecución (~200MB de descarga de Chromium), luego captura el gráfico con deviceScaleFactor: 2.
Paso 6: Resumen de salida
## Gráfico: [título]
Fecha: [AAAA-MM-DD] | Tipo: [chart_type] | Estilo: [style]
Dimensiones: [W×H]px → PNG: [2W×2H]px @2× retina
Archivos
Fuente: chart/[slug]/chart.html
Salida: chart/[slug]/chart.png
Tamaño: [X] KB
Lista de verificación
- [ ] El título expresa la conclusión claramente
- [ ] Las etiquetas de datos son legibles en el tamaño de visualización
- [ ] El resaltado es visible en el punto de datos correcto
- [ ] Atribución de la fuente presente en el pie de página
Consejos rápidos (mostrar cuando el usuario pida orientación)
"Proporciona datos estructurados — JSON o CSV, no descripciones en prosa."
"Nombra el tipo de gráfico explícitamente. 'gráfico de barras que compara Q1–Q4' no 'un gráfico que muestra trimestres'."
"Especifica la historia de los datos. 'resalta Q4 que superó a todos los demás' da el contexto de la anotación."
✅ Bueno: "Crea un gráfico de líneas. Título: 'De $12k a $95k ARR en 12 Meses'. Datos: [12, 18, 22, 25, 31, 38, 44, 52, 61, 68, 78, 95] (Ene–Dic 2024). Resalta Diciembre. Fuente: CRM Interno. Estilo: electric-burst."
❌ Malo: "haz un gráfico sobre el crecimiento de nuestra empresa"