plotagents 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/AGENTS.md +5 -1
  2. package/README.md +18 -1
  3. package/bin/plotagents.js +3 -1
  4. package/dist/plotagents-geo.js +1 -1
  5. package/dist/plotagents.js +1857 -110
  6. package/docs/charts.md +86 -1
  7. package/docs/es/agents.md +102 -0
  8. package/docs/es/api.md +172 -0
  9. package/docs/es/charts.md +436 -0
  10. package/docs/es/colorscales.md +46 -0
  11. package/docs/es/dashboards.md +79 -0
  12. package/docs/es/getting-started.md +142 -0
  13. package/docs/es/rendering.md +47 -0
  14. package/docs/es/themes.md +116 -0
  15. package/index.d.ts +92 -8
  16. package/package.json +3 -2
  17. package/schema.json +359 -9
  18. package/skills/plotagents/SKILL.md +105 -0
  19. package/src/charts/bar.js +13 -5
  20. package/src/charts/chord.js +3 -1
  21. package/src/charts/choropleth.js +3 -1
  22. package/src/charts/corr.js +512 -0
  23. package/src/charts/dist.js +2 -1
  24. package/src/charts/donut.js +3 -1
  25. package/src/charts/kpi.js +2 -2
  26. package/src/charts/line.js +19 -7
  27. package/src/charts/network.js +13 -8
  28. package/src/charts/pointmap.js +9 -6
  29. package/src/charts/regplot.js +9 -3
  30. package/src/charts/sankey.js +1 -1
  31. package/src/charts/scatter.js +4 -2
  32. package/src/charts/sunburst.js +2 -2
  33. package/src/charts/table.js +882 -0
  34. package/src/charts/treemap.js +2 -2
  35. package/src/charts/wordcloud.js +17 -3
  36. package/src/colorscales.js +12 -1
  37. package/src/dashboard.js +21 -8
  38. package/src/data.js +2 -0
  39. package/src/facet.js +4 -3
  40. package/src/format.js +36 -13
  41. package/src/frame.js +8 -5
  42. package/src/geo/projection.js +4 -2
  43. package/src/hierarchy.js +6 -0
  44. package/src/i18n.js +107 -0
  45. package/src/index.js +17 -4
  46. package/src/infer.js +9 -1
  47. package/src/lint.js +66 -4
  48. package/src/runtime.js +94 -6
  49. package/src/scale.js +3 -2
  50. package/src/schema.js +54 -11
  51. package/src/stats.js +5 -3
  52. package/src/svg.js +4 -1
  53. package/src/text.js +5 -0
@@ -0,0 +1,436 @@
1
+ # Referencia de gráficos
2
+
3
+ Cada gráfico recibe una especificación con `data` (filas) y los nombres de campo de cada rol. Las opciones de abajo son propias de cada tipo; las [opciones comunes](#common-options) valen para todos. Cada opción también está descrita en [`schema.json`](../schema.json).
4
+
5
+ ## Opciones comunes
6
+
7
+ | Opción | Significado |
8
+ |---|---|
9
+ | `type` | Tipo de gráfico o alias. Omítelo (o usa `"auto"`) para inferirlo de los datos. |
10
+ | `data` | Arreglo de objetos fila. También acepta `{ A: 1, B: 2 }` y arreglos de números. |
11
+ | `title` | El hallazgo, como una oración. Se divide en líneas; nunca se trunca. |
12
+ | `subtitle` | Cómo leer el gráfico: medida, unidad, período. |
13
+ | `source` | Fuente de los datos, en letra pequeña abajo a la derecha. |
14
+ | `series` / `hue` | Campo que divide los datos en grupos de color. |
15
+ | `highlight` | Un nombre o una lista: destaca esos y atenúa el resto. |
16
+ | `colors` | `{ "name": "#hex" }` para fijar el color de una entidad. |
17
+ | `format`, `xFormat`, `yFormat` | `"number" \| "compact" \| "percent" \| "currency" \| "integer"` o `{ prefix, suffix, decimals, currency }`. |
18
+ | `locale` | `"es"` (por defecto: `1.234,5`), `"en"` (`1,234.5`), `"pt"`, `"fr"` (`1 234,5`), `"de"` o `"auto"`: se reconoce a partir del título, el subtítulo, los nombres de columna y las categorías. Las tablas usan `"auto"` cuando omites `locale`. |
19
+ | `xLabel`, `yLabel` | Títulos de los ejes (horizontales, nunca rotados). |
20
+ | `xScale`, `yScale` | `"log"` donde tenga sentido. |
21
+ | `xDomain`, `yDomain` | `[min, max]` fijo. |
22
+ | `refLines` | `[{ y: 600, label: "Target" }]` (o `x`, o `value` para barras). |
23
+ | `facet` (`col`), `columns`, `sharey`, `sharex` | Múltiplos pequeños, un panel por valor. |
24
+ | `time`, `timeValue` | Un período a la vez: el más reciente por defecto, o `timeValue`. El período se agrega al subtítulo. |
25
+ | `slider`, `play` | Controles de tiempo en el navegador, **desactivados por defecto**. `slider: true` agrega botones de período cuando hay pocos períodos (≤ 8) y un deslizador mínimo cuando hay muchos; los gráficos de líneas y de áreas reciben un rango con dos tiradores. `"buttons"` o `"slider"` fuerzan uno; `play: false` oculta el botón de reproducir. |
26
+ | `compare` | Con `time`: compara contra `"previous"` (por defecto), `"first"`, un período dado o `false`. |
27
+ | `theme`, `palette`, `ramp` | Consulta [temas](themes.md). |
28
+ | `colorScale` (`cmap`) | Cualquier mapa de colores de matplotlib (`"viridis"`, `"RdBu_r"`…) o tu propia lista de colores. Consulta [escalas de color](colorscales.md). |
29
+ | `font`, `textScale` | Tipografía (nombre de Google Fonts, pila CSS o `{ family, import }`) y un multiplicador del tamaño del texto (alias `fontScale`, `labelScale`). Fíjalos para todos los gráficos con `configure()`. |
30
+ | `width`, `height`, `padding` | Píxeles. La altura es automática en barras horizontales, mapas de calor, tarjetas KPI y en treemaps, sunbursts y radares angostos. |
31
+ | `legend` | Posición: `"top"` (por defecto), `"bottom"`, `"left"`, `"right"` o `false` para ocultarla. Las leyendas laterales pasan abajo por debajo de 520 px. Fijar una posición también fuerza una leyenda donde el gráfico, de otro modo, rotularía las líneas directamente. |
32
+ | `exclude` | Un nombre o una lista: quita esas entradas de la leyenda (series, grupos del primer nivel) y recalcula ejes, proporciones y diseño. Los colores se mantienen y las entradas excluidas siguen en la leyenda, atenuadas y tachadas. |
33
+
34
+ ### En el navegador
35
+
36
+ - **Tooltips**: aparecen junto a la marca bajo el puntero, nunca sobre el título o el subtítulo, y no salen del gráfico. Su encabezado toma el color de la marca, con texto blanco u oscuro según el contraste.
37
+ - **Leyenda**: al pasar el puntero sobre una clave se destacan sus marcas; un clic fija el destaque. **Shift + clic** excluye esa entrada (haz clic de nuevo para recuperarla). En treemaps y sunbursts, un clic simple amplía todo el gráfico a esa categoría; haz clic otra vez, usa la ruta de navegación o presiona Esc para volver.
38
+
39
+ ## Comparar
40
+
41
+ ### `bar` · `column` · `barplot` · `count` (`countplot`)
42
+
43
+ `x` categoría, `y` valor. Columnas para pocas categorías cortas, barras horizontales cuando las etiquetas son largas o hay muchas; valores en la punta de las barras y sin eje cuando todas las barras tienen etiqueta.
44
+
45
+ | Opción | Significado |
46
+ |---|---|
47
+ | `stack` | `true` apila las series; `"percent"` apila al 100%. |
48
+ | `horizontal` | Fuerza la orientación. |
49
+ | `sort` | `"desc"` (por defecto para categorías), `"asc"`, `"none"`, `"alpha"`. Los años y las fechas mantienen su orden. |
50
+ | `top` | Conserva las N más grandes y agrupa el resto en "Otros" (automático sobre 40 categorías). |
51
+ | `ci`, `errorbar`, `estimator` | `barplot`: media ± IC del 95% (`"ci"`), barras de error `"sd"` o `"se"`. |
52
+ | `aggregate` | `"sum"` (por defecto), `"mean"`, `"max"`, `"min"`, `"count"`, `"first"`, `"last"`. `countplot` cuenta filas. |
53
+ | `labels` | `true`, `false` o `"auto"`. |
54
+ | `barWidth` | Grosor máximo de la barra en px. |
55
+
56
+ ## Tiempo
57
+
58
+ ### `line` · `area`
59
+
60
+ `x` fecha (`"2024-03"`, `"2024-03-15"`, `Date`), año o número; `y` valor o lista de campos (tabla ancha → series).
61
+
62
+ | Opción | Significado |
63
+ |---|---|
64
+ | `labels` | Etiquetas al final (activadas por defecto para cada serie que cabe; las etiquetas que chocan reciben líneas guía). |
65
+ | `annotations` | `[{ x: "2024-01", label: "New branch" }]`. |
66
+ | `index` | Lleva cada serie a base 100 en su primer valor. |
67
+ | `ci`, `errorbar`, `estimator` | `lineplot` de seaborn: media y banda de confianza cuando hay varias filas por x. |
68
+ | `curve` | `"linear"` (por defecto), `"smooth"` (monótona, nunca inventa picos), `"step"`. |
69
+ | `zero` | Fuerza el eje y a incluir el cero (activado por defecto en áreas y cuando cuesta poco). |
70
+ | `points` | Marcadores en cada punto (automático en series cortas). |
71
+ | `stack` | Áreas: `true` (por defecto con series), `"percent"` o `false` para superponerlas. |
72
+ | `fill` | Línea única: `false` quita el degradado suave. |
73
+ | `xRange` | Rango visible `[from, to]`; es lo que fija el deslizador de rango (`slider: true`). |
74
+
75
+ ## Proyectos
76
+
77
+ ### `gantt` (`timeline`, `roadmap`, `schedule`, `cronograma`)
78
+
79
+ Una fila por tarea: `task`, `start` y `end` (o `duration`). Fechas como `"2026-03-02"` o `"2026-03"`, o números simples para semanas o sprints. Las fechas con día son **inclusivas**: una tarea del 3 al 5 de marzo dura 3 días. Una fila sin `end`, o con `end` igual a `start`, es un hito y se dibuja como un rombo con su fecha.
80
+
81
+ El eje de tiempo va arriba, como en un calendario. Con `group`, cada fase abre con su nombre y una barra resumen delgada en su color, así que no hace falta leyenda. La parte rellena de cada barra es el avance; el resto queda en un tono claro del mismo color. Las dependencias son flechas delgadas en ángulo recto detrás de las barras.
82
+
83
+ ```js
84
+ plot({
85
+ type: 'gantt',
86
+ title: 'The project goes live on June 15; the ERP integration is the critical path',
87
+ data: [
88
+ { phase: 'Design', task: 'Data model', start: '2026-02-09', end: '2026-03-06', progress: 80 },
89
+ { phase: 'Build', task: 'ERP integration', start: '2026-03-16', end: '2026-04-30', progress: 10, after: 'Data model' },
90
+ { phase: 'Launch', task: 'Go-live', start: '2026-06-15' },
91
+ ],
92
+ task: 'task', start: 'start', end: 'end', group: 'phase', progress: 'progress', dependsOn: 'after',
93
+ today: '2026-03-12', locale: 'en',
94
+ });
95
+ ```
96
+
97
+ | Opción | Significado |
98
+ |---|---|
99
+ | `task`, `start`, `end` | Nombres de campo (se detectan cuando se llaman `task`/`tarea`, `start`/`inicio`, `end`/`fin`). |
100
+ | `duration` | Duración en vez de `end`: días en un eje de fechas, unidades en uno numérico. |
101
+ | `group` (`series`, `hue`) | Fase o equipo: una sección de color por valor. `groupHeaders: false` usa una leyenda de colores en su lugar. |
102
+ | `progress` | 0–1 o 0–100. Se muestra como la parte rellena, con el porcentaje dentro de la barra cuando cabe. |
103
+ | `dependsOn` | Campo con el nombre de la tarea predecesora, o varias: `"A, B"`. |
104
+ | `milestone` | Campo booleano que fuerza hitos. |
105
+ | `today` | Una fecha (o `true`) para la línea de "hoy", rotulada en el eje. `refLines: [{ x, label }]` agrega más. |
106
+ | `dashed`, `dashedLabel` | Achura las tareas tentativas: un campo (`dashed: "tentative"`), nombres o fases. |
107
+ | `highlight` | Destaca una tarea o una fase completa. |
108
+ | `sort` | `"start"` ordena las tareas por fecha (por defecto: el orden de los datos, agrupado por fase). |
109
+ | `rowHeight`, `xLabel`, `unit` | Alto de fila en px; título del eje y unidad para ejes numéricos. |
110
+ | `collapsed`, `collapsible` | Fases dibujadas como una sola fila resumen (avance ponderado por duración, rombos de hitos): una lista de nombres o `true`. En el navegador, un clic en una fase la pliega o despliega (Alt + clic actúa sobre todas); el eje de tiempo y la columna de nombres no se mueven. `collapsible: false` oculta los chevrones. |
111
+ | `critical`, `criticalColor` | Remarca la ruta crítica: tareas sin holgura a lo largo de `dependsOn` (días hábiles en fechas con día), con flechas de acento. |
112
+ | `weekends` | Franjas de fin de semana en planes con fechas de día de hasta 120 días; `false` las desactiva. |
113
+
114
+ `lint` advierte sobre tareas que terminan antes de empezar y sugiere una línea `today`. El texto alternativo nombra el período total, la tarea más larga, los hitos y el avance general ponderado por duración.
115
+
116
+ ## Relaciones
117
+
118
+ ### `scatter` · `bubble`
119
+
120
+ `x`, `y` numéricos; `size` para burbujas (proporcional al área); campo `label` con `labels` (número de puntos o lista de nombres) para etiquetas selectivas; `trend: true` agrega una recta de mínimos cuadrados con R².
121
+
122
+ ### `regplot` (`lmplot`) · `residplot` · `jointplot`
123
+
124
+ | Opción | Significado |
125
+ |---|---|
126
+ | `ci` | Nivel de la banda de confianza (95 por defecto, `false` para ocultarla). |
127
+ | `order` | Grado del polinomio. |
128
+ | `lowess` (+ `frac`) | Regresión local en vez de una recta. |
129
+ | `logx` | Ajusta `y ~ log(x)` y usa un eje x logarítmico. |
130
+ | `scatter` | `false` oculta los puntos. |
131
+ | `truncate` | Dibuja el ajuste solo dentro del rango de los datos (por defecto `true`). |
132
+ | `annotate` | Muestra R² y n. |
133
+ | `label`, `labels` | Nombra puntos seleccionados. |
134
+ | `kind` | `jointplot`: `"scatter"` o `"reg"`. |
135
+ | `marginal` | `jointplot`: `"hist"` o `"kde"`. |
136
+
137
+ ### `corr` (`correlation`, `corrplot`) · `clustermap`
138
+
139
+ Matriz de correlación de los campos numéricos, que la biblioteca calcula a partir de las filas (casos completos por pares, como `df.corr()`). Cada par aparece una vez, bajo la diagonal, en una escala divergente fija de −1 a 1, así que el mismo color significa la misma intensidad en todos los gráficos. Las etiquetas largas se giran 45°. El tooltip muestra r, n y el valor p.
140
+
141
+ ```js
142
+ plot({ type: 'corr', data, vars: ['income', 'schooling', 'poverty', 'density'], alpha: 0.05,
143
+ title: 'Where schooling is higher, income is higher (r = 0.85) and poverty lower (r = −0.81)' });
144
+ ```
145
+
146
+ | Opción | Significado |
147
+ |---|---|
148
+ | `vars` | Campos a relacionar, en orden. Por defecto: todos los campos numéricos. `y: [...]` también funciona. |
149
+ | `method` | `"pearson"` (por defecto) o `"spearman"` (rangos: relaciones monótonas, robusta ante valores atípicos). |
150
+ | `triangle` | `"lower"` (por defecto) o `"full"`. |
151
+ | `cluster` | Reordena por agrupamiento jerárquico (enlace promedio sobre 1 − r) y dibuja el dendrograma arriba. `type: "clustermap"` lo activa y muestra la matriz completa; `dendrogram: false` mantiene el orden sin el árbol. |
152
+ | `alpha` | Pone en gris los pares con p ≥ alpha (z de Fisher) y agrega una clave a la leyenda. |
153
+ | `order` | Orden de las variables (lista). |
154
+ | `highlight` | Una variable: su fila y su columna se mantienen y el resto se atenúa. |
155
+ | `labelsFor` | Nombres para mostrar, p. ej. `{ areas_verdes: "Green space" }`. |
156
+ | `x`, `y`, `value` | Dibuja una matriz que ya calculaste, en formato largo (una fila por par). |
157
+
158
+ ### `pairplot` (`pairs`, `splom`, `scattermatrix`)
159
+
160
+ Matriz de diagramas de dispersión: cada par como dispersión bajo la diagonal, la distribución de cada variable en la diagonal (histograma, o densidad con `hue`) y la r de Pearson sobre ella, teñida según la intensidad y dividida por grupo de `hue` cuando hay espacio. Los ejes se comparten por columna y por fila; las marcas de los ejes solo van en los bordes exteriores.
161
+
162
+ | Opción | Significado |
163
+ |---|---|
164
+ | `vars` | Campos a graficar (3–6 se leen mejor). Por defecto: los primeros 6 campos numéricos. |
165
+ | `hue` | Color por grupo, con leyenda. |
166
+ | `diag` | `"hist"` o `"kde"`. |
167
+ | `upper` | `"corr"` (por defecto), `"scatter"` o `false`. `corner: true` conserva solo el triángulo inferior. |
168
+ | `kind` | `"reg"` agrega una recta de mínimos cuadrados a cada panel de dispersión. |
169
+ | `radius`, `bins`, `panelSize` | Radio de los puntos, intervalos del histograma, tamaño máximo del panel. |
170
+
171
+ ### `radar` (`spider`)
172
+
173
+ Datos largos (`axis`, `value`, `series`) o anchos (`axes: [fields]`, una fila por serie con `label`). `normalize` escala cada eje a su propio máximo (los tooltips conservan los valores originales); `grid: "polygon" | "circle"`; `domain`. Funciona mejor con ≤ 4 series.
174
+
175
+ ## Distribuciones
176
+
177
+ ### `hist` (`histplot`) · `kde` (`kdeplot`) · `ecdf`
178
+
179
+ | Opción | Significado |
180
+ |---|---|
181
+ | `bins`, `binWidth` | Número o ancho de los intervalos (por defecto: Freedman–Diaconis, redondeado a bordes limpios). |
182
+ | `stat` | `"count"`, `"percent"`, `"density"`, `"probability"`. |
183
+ | `kde` | Superpone una curva de densidad sobre el histograma. |
184
+ | `multiple` | Con `hue`: `"layer"`, `"stack"`, `"dodge"`, `"fill"`. |
185
+ | `element` | `"bars"` o `"step"`. |
186
+ | `cumulative` | Distribución acumulada. |
187
+ | `fill`, `bwAdjust`, `cut`, `clip`, `rug` | Opciones de densidad. |
188
+ | `complementary` | `ecdf`: grafica 1 − F(x). |
189
+
190
+ ### `box` · `violin` · `strip` · `swarm` (`beeswarm`) · `point` (`pointplot`)
191
+
192
+ `x` categoría e `y` medida (intercámbialos, o usa `horizontal`, para la versión horizontal). Las etiquetas largas o muchas categorías cambian a horizontal de forma automática.
193
+
194
+ | Opción | Significado |
195
+ |---|---|
196
+ | `points` | Caja: superpone las observaciones. |
197
+ | `showMeans` | Marca la media. |
198
+ | `inner` | Violín: `"box"` (por defecto), `"quartiles"`, `"points"`, `"none"`. |
199
+ | `split` | Violín con dos niveles de `hue`: una mitad cada uno. |
200
+ | `scale` | Ancho del violín: `"width"`, `"area"`, `"count"`. |
201
+ | `jitter`, `dodge`, `radius` | Disposición de strip y swarm. |
202
+ | `estimator`, `errorbar` | Gráfico de puntos: `"mean"`/`"median"`/`"sum"` y `"ci"`/`"sd"`/`"se"`/`"pi"`. |
203
+ | `order`, `hueOrder` | Orden de las categorías y de los grupos. |
204
+
205
+ ## Partes de un todo
206
+
207
+ ### `treemap` · `sunburst`
208
+
209
+ `path: [level1, level2, …]` de cualquier profundidad (o `group` + `label`), `value`. Las filas con la misma ruta se suman.
210
+
211
+ | Opción | Significado |
212
+ |---|---|
213
+ | `depth` | Muestra solo los primeros N niveles. |
214
+ | `focus` | Ruta a la que entrar, p. ej. `["Mining"]`. En el navegador lo hace un clic en un grupo o en su clave de la leyenda (la categoría llena todo el gráfico), y la ruta de navegación o Esc te devuelven. |
215
+ | `exclude` | Grupos del nivel 1 que se dejan fuera; el resto llena el plano y las proporciones se recalculan. En el navegador lo hace Shift + clic en una clave de la leyenda. |
216
+ | `showValues` | Agrega los valores a las etiquetas. |
217
+ | `colorBy` | `"change"` colorea los rectángulos según el % de cambio respecto del período de comparación (crecimiento en tonos fríos, caída en cálidos); `"label"` colorea por elemento un treemap de un solo nivel. |
218
+ | `shade` | Pasos sutiles de luminosidad dentro de cada categoría. Desactivado por defecto: un color sólido por categoría. |
219
+ | `compareData` | Filas de un período anterior para calcular cambios sin `time`. |
220
+ | `hole`, `centerValue`, `centerLabel` | Centro del sunburst. |
221
+
222
+ Las áreas son exactas: no hay franjas de encabezado que le quiten espacio a un grupo. Las categorías se distinguen por un color sólido, una separación de 4 px entre grupos del nivel 1 (2 px y 1 px en niveles más profundos) y la leyenda, que muestra la proporción de cada grupo. Con `time`, cada rectángulo también muestra su cambio respecto del período anterior (▲ 12%). Los colores pertenecen al grupo del nivel 1 y se calculan sobre el árbol completo y todos los períodos, así que nunca cambian al entrar en un grupo ni al mover el deslizador.
223
+
224
+ ### `pyramid` (`populationpyramid`)
225
+
226
+ Grupos de edad (`age` o `y`), dos lados (`sex`, `series` o `hue`) y un valor. Barras enfrentadas con las edades en la columna central; `percent: true` para proporciones de la población; `compareData`, o `time` + `compare`, dibuja un año anterior como un contorno escalonado.
227
+
228
+ ### `donut` · `pie`
229
+
230
+ `label`, `value`. Más de `maxSlices` (6) se agrupan en "Otros".
231
+
232
+ | Opción | Significado |
233
+ |---|---|
234
+ | `labels` | `"auto"`, `"inside"`, `"outside"` (líneas guía), `"legend"`, `false`. |
235
+ | `half` | Semicírculo. |
236
+ | `thickness` | Grosor del anillo (el pie es `1`). |
237
+ | `sort` | `"desc"` o `"none"`. |
238
+ | `centerValue`, `centerLabel` | Centro de la dona. |
239
+
240
+ ## Mapas
241
+
242
+ ### `choropleth` (`map`) · `symbolmap` (`bubblemap`)
243
+
244
+ Las regiones de Chile (`geo: "chile"`, por defecto), los estados de EE. UU. (`geo: "usa"`) y los países del mundo (`geo: "world"`) vienen con la biblioteca, y **cualquier geometría propia** también funciona (ver abajo). `key` es el campo de los datos que se empareja y `value` la medida; un `value` de texto (un partido, un idioma, una zona) colorea por categoría con una leyenda de muestras de color. El emparejamiento es tolerante: nombres, códigos y alias, sin importar tildes, mayúsculas ni "Región de" ("RM", "Biobio", "XIII", "Aisén", "ARG", "EE.UU.", "TX", "Nueva York", "48").
245
+
246
+ **Estados Unidos.** 50 estados y DC en una proyección cónica equivalente de Albers (`albersUsa`, la misma composición que `d3.geoAlbersUsa`): Alaska (a 0,35×) y Hawái van en recuadros abajo a la izquierda con un marco fino punteado; Puerto Rico recibe su propio recuadro solo cuando los datos lo incluyen (`territories: true` lo fuerza). Cada estado muestra su código postal, más el valor cuando cabe; los estados pequeños del noreste (VT, NH, MA, RI, CT, NJ, DE, MD, DC) se rotulan en una columna a la derecha con líneas guía. `values: false` deja solo los códigos; `labels: false` los quita.
247
+
248
+ | Opción | Significado |
249
+ |---|---|
250
+ | `geo` | `"chile"`, `"usa"`, `"world"`, un nombre registrado con `registerGeo()`, un objeto GeoJSON o TopoJSON, o (en el navegador) una URL. |
251
+ | `featureKey`, `nameKey` | Propiedad de la geometría que se empareja, y la que tiene el nombre para mostrar (ambas se detectan si las omites). |
252
+ | `fitTo` | Amplía a regiones, a una caja `[lonMin, latMin, lonMax, latMax]` o a `"markers"`. |
253
+ | `markers`, `layers` | Puntos por lon/lat encima; líneas, polígonos o puntos GeoJSON adicionales sobre o bajo el mapa. |
254
+ | `scale`, `classes` | `"quantile"` (por defecto), `"equal"`, `"continuous"` o una lista de cortes; 5 clases por defecto. |
255
+ | `midpoint` / `diverging` | Clases divergentes en torno a un valor (p. ej. 0 para cambios). |
256
+ | `missing` | `"plain"` quita el achurado y la entrada "Sin datos" de la leyenda. |
257
+ | `labels`, `bars` | Chile recibe una columna de norte a sur con nombre, barra y valor; `bars: false` oculta las barras. Los mapas del mundo rotulan los N primeros. |
258
+ | `projection` | `"mercator"`, `"equalEarth"`, `"albersUsa"`, `"equirectangular"`, `"conic"`, `"identity"`, `"planar"` o `{ type, parallels, rotate }` (se elige de forma automática). |
259
+ | `borderColor`, `borderWidth` | Bordes de las regiones. |
260
+ | `dashed`, `dashedLabel` | Achura algunas regiones (cifras preliminares, zonas en disputa): nombres, códigos o un campo de los datos. Conservan el color de su clase. |
261
+ | `maxRadius` | `symbolmap`: radio del círculo más grande. |
262
+
263
+ #### Tu propia geometría: GeoJSON y TopoJSON
264
+
265
+ `geo` acepta lo que tengas: un `FeatureCollection` GeoJSON, un solo `Feature`, un arreglo de features o una geometría suelta, o un `Topology` TopoJSON (`geoObject` elige el objeto; por defecto, el primero). Los polígonos se rellenan, las líneas se trazan y los puntos se vuelven círculos coloreados por valor. En el navegador, `render()` también acepta una URL y la descarga una sola vez: `geo: "/maps/comunas.topojson"`. En Node, lee el archivo y pasa el objeto.
266
+
267
+ ```js
268
+ import { readFileSync } from 'node:fs';
269
+ const comunas = JSON.parse(readFileSync('comunas-biobio.geojson', 'utf8'));
270
+ plot({
271
+ type: 'choropleth',
272
+ geo: comunas, // properties: { CUT_COM: "08101", NOM_COM: "Concepción", … }
273
+ data: [{ comuna: 'Concepción', poverty: 7.1 }, { comuna: 'Coronel', poverty: 12.4 }],
274
+ key: 'comuna', value: 'poverty', // NOM_COM is found automatically
275
+ markers: { data: hospitals, lon: 'lon', lat: 'lat', label: 'name', legend: 'Hospital' },
276
+ layers: [{ geo: rivers, label: 'Biobío river', stroke: '#5BA3E0', width: 2 }],
277
+ fitTo: ['Concepción', 'Talcahuano', 'Hualpén'],
278
+ });
279
+ ```
280
+
281
+ - **Emparejamiento sin configuración.** Las filas se emparejan con las claves habituales (código, id, ISO-3, nombre, alias); si algunas filas no calzan, plotagents encuentra la propiedad de la geometría cuyos valores sí calzan (`NOM_COM`, `CUT_REG`, `GEOID`, `ADMIN`…). `featureKey` fuerza una. Los nombres para mostrar salen de `nameKey`, o de la primera de `name`, `NOMBRE`, `NAME_1`, `ADMIN` y similares.
282
+ - **Proyecciones.** Se eligen según la geometría: Mercator para un país o una región, Equal Earth para el mundo y un plano cartesiano simple cuando las coordenadas no son longitudes y latitudes (metros UTM, por ejemplo). Fija `projection` en `"equirectangular"`, `"conic"` (o `{ type: "conic", parallels: [-30, -45], rotate: 72 }`), `"identity"` (coordenadas en píxeles, y hacia abajo, para mapas dibujados a mano o de teselas) o `"planar"` (y hacia arriba).
283
+ - **Encuadre.** `fitTo` amplía a algunas regiones, a una caja `[lonMin, latMin, lonMax, latMax]` o a `"markers"`; el resto del mapa queda recortado como contexto. `tall: false` desactiva la columna lateral para formas alargadas.
284
+ - **Marcadores.** `markers: [{ lon, lat, label, value }]` o `{ data, lon, lat, label, size, color, legend, maxRadius }`: puntos encima, proporcionales al área cuando hay un `size`, con etiquetas ubicadas donde no chocan.
285
+ - **Capas.** `layers: [{ geo, label, stroke, width, fill, dash, opacity, below }]` superpone ríos, caminos, rutas, fallas o límites de distritos (o los dibuja bajo las regiones con `below: true`). Un `label` agrega una clave a la leyenda.
286
+ - **Estilo.** `borderColor`, `borderWidth`, `pointRadius`, `lineWidth`, `ramp` (una rampa secuencial con nombre o propia), `palette` y `colors` (categorías).
287
+ - **Regístrala una vez y úsala por nombre.** `registerGeo('biobio', comunas, ['comunas-biobio'])` y luego `geo: 'biobio'` en cada especificación, lo que mantiene cortas las llamadas a herramientas de un agente.
288
+
289
+ En el navegador (`render()` o `toHTML()`), los mapas son interactivos: al pasar el puntero se remarca la forma; un clic en una región la amplía hasta encuadrarla (vuelve con otro clic, doble clic, Esc o el botón de reinicio); Ctrl/⌘ + rueda amplía en torno al puntero (la rueda sola sigue desplazando la página, como recomienda `chartjs-plugin-zoom` con su `modifierKey`); el gesto de pellizcar amplía en pantallas táctiles; arrastrar desplaza el mapa ampliado; los botones +/− funcionan desde el teclado. Cada clase de color de la leyenda es una clave: pasa el puntero para iluminar sus regiones y haz clic para fijarlas. Con `time`, el color de cada región transita hacia el del período siguiente en vez de saltar, y la ampliación se mantiene mientras se reproduce el deslizador.
290
+
291
+ `lint` advierte sobre filas que no calzan con ninguna región o país. Fuentes: Natural Earth 1:110m (dominio público) para el mundo; Biblioteca del Congreso Nacional para las regiones de Chile (atribución en `src/geo/data/chile-regiones.js`); límites cartográficos 1:10m de la Oficina del Censo de EE. UU. (dominio público) vía us-atlas 3.0.1 para los estados de EE. UU. En el navegador, `dist/plotagents-geo.js` agrega las geometrías; `toHTML()` lo incluye solo cuando una página tiene un mapa.
292
+
293
+ ### `pointmap` (`dotmap`, `tilemap`)
294
+
295
+ Puntos por longitud/latitud sobre un mapa base real en Web Mercator. Los campos `lon`/`lat` se encuentran por nombre (lon, lng, longitude, longitud / lat, latitude, latitud). `size` → área del círculo (clave en la esquina, con el más grande rotulado); `hue` → colores por categoría con leyenda; `value` numérico → rampa secuencial (`midpoint` para divergente, `colorScale` para cualquier mapa de matplotlib). La ampliación y el centro se ajustan a los puntos (`zoom`, `center: [lon, lat]` para fijarlos).
296
+
297
+ ```js
298
+ plot({ type: 'pointmap', data: cities, lon: 'lon', lat: 'lat', size: 'population', label: 'city',
299
+ title: 'Santiago concentrates 40% of the population', source: 'INE' });
300
+ ```
301
+
302
+ | Opción | Significado |
303
+ |---|---|
304
+ | `tiles` | `"osm"` (por defecto, OpenStreetMap), `"esri-gray"`, `"esri-dark"`, `"carto-light"` / `"carto-dark"` (clave gratuita: carto.com/basemaps/apikey), `"mapbox-light"` / `"mapbox-dark"` (token público `pk.` en `tilesKey`; los tokens `sk.` se rechazan), `{ url, attribution, subdomains, maxZoom, retina }` o `false` (solo el contorno vectorial del mundo). |
305
+ | `tilesKey` | Clave o token para CARTO, Mapbox o Esri. |
306
+ | `tileStyle` | `"muted"` (por defecto) desatura las teselas y las tiñe con el tema (un mapa nocturno azul marino en `noche`); `"original"` las deja tal cual. |
307
+ | `tileOpacity`, `zoom`, `center` | Opacidad de las teselas, nivel de ampliación fijo y centro `[lon, lat]`. |
308
+ | `labels`, `labelValues` | Cuántos puntos nombrar (los 8 más grandes por defecto), o una lista; `labelValues: false` oculta los valores. |
309
+
310
+ La atribución siempre se dibuja. Las teselas son imágenes externas: se ven en SVG en línea, `render()` y `toHTML()`, **no** cuando el SVG se usa como `<img>`, se abre desde un conversor a PDF o sin conexión; en esos casos el contorno vectorial del mundo que está debajo sigue dando contexto. `await embedTiles(svg)` las incrusta como URI data: (no para OSM, cuya política de teselas prohíbe las descargas masivas; usa `esri-gray` o tu propio servidor). Para mucho tráfico, usa un proveedor con clave o tus propias teselas: los servidores de OSM son donados.
311
+
312
+ ## Formas discontinuas: estimaciones, proyecciones, cifras provisionales
313
+
314
+ Algunas cifras todavía no son firmes. En vez de un color nuevo (que se lee como una categoría nueva), plotagents mantiene el color de la forma y cambia su textura: un relleno achurado (franjas del color sobre un tono claro del mismo) y un contorno discontinuo con contraste 3:1. Las líneas reciben un trazo discontinuo con puntos huecos. Funciona en `bar`, `line`, `area`, `donut`/`pie`, `choropleth` y `symbolmap`.
315
+
316
+ | Opción | Significado |
317
+ |---|---|
318
+ | `dashed` | Un nombre de categoría, serie o región, o una lista (`["Texas", "Ohio"]`); un campo de los datos cuyas filas verdaderas van discontinuas (`"provisional"`, acepta `true`, `1`, `"sí"`, `"yes"`, `"x"`); `{ field, equals }`; o `true` para todo. |
319
+ | `dashedFrom` | En un eje de tiempo (`line`, `area`, `bar`): el primer x proyectado. La línea pasa a discontinua desde el último punto firme, los puntos proyectados son huecos, las áreas apiladas se achuran con el color de cada serie y una banda suave con `dashedLabel` marca el tramo. |
320
+ | `dashedLabel` | Qué significa: "Proyección", "Preliminar", "Estimación". Es la entrada de la leyenda (pasa el puntero para iluminar solo las formas discontinuas), la etiqueta de la banda y una línea en el tooltip. Por defecto es "Estimado"/"Estimate"; `lint` sugiere fijarlo. |
321
+
322
+ Esto sigue el consejo de Chart.js de usar patrones (como hace `patternomaly`) y `segment.borderDash` para los tramos proyectados, de modo que la distinción nunca depende solo del color.
323
+
324
+ ## Redes y flujos
325
+
326
+ ### `network` (`graph`)
327
+
328
+ Aristas en `data` (`from`, `to`, `value` opcional), `nodes` opcionales con `id`, `group` y `size`. Área del nodo ∝ peso (grado ponderado por defecto), color por grupo; las aristas se curvan con el color de su nodo de origen (`curveness`, por defecto 0.3; `0` = rectas) y miden 1 px de ancho sin un campo de valor. Los nodos más grandes llevan su nombre (tinta con halo, sin chocar entre sí ni con un nodo); todos los nombres están en el tooltip. Al pasar el puntero sobre un nodo se iluminan sus vecinos y sus aristas.
329
+
330
+ `layout` elige la disposición (todas deterministas, todas ajustadas al área):
331
+
332
+ | `layout` | Disposición |
333
+ |---|---|
334
+ | `force` (por defecto) | Dirigida por fuerzas (Barnes–Hut sobre 150 nodos). |
335
+ | `circular` (`circle`) | En torno a un círculo por grupo, con un espacio entre grupos y los nombres rotados hacia afuera. |
336
+ | `radial` | Un árbol desde `focus` (o el nodo más conectado); cada anillo es un paso más lejos. Los enlaces fuera del árbol son tenues. |
337
+ | `concentric` | Anillos por peso, el más grande al centro, por grupo dentro de cada anillo. |
338
+ | `clusters` | Un grupo en forma de girasol por grupo, con espacio ∝ √tamaño. |
339
+ | `tree` | El árbol radial extendido en plano (de izquierda → derecha en áreas anchas), con conectores en forma de S. |
340
+ | `grid` | Una cuadrícula ordenada por grupo y peso, con todos los nodos nombrados. |
341
+
342
+ `labels` (número, lista o `true`), `highlight` (un nodo y sus vecinos), `focus` (el id de un nodo: va al centro, o a la raíz en `radial`/`tree`, y se destaca con sus vecinos, todos rotulados; en el navegador lo hace un clic con una transición suave, y un segundo clic o Esc vuelve), `iterations`, `maxRadius`, `curveness`.
343
+
344
+ ### `chord`
345
+
346
+ Filas `from`, `to`, `value`, o `matrix` + `names`. `directed` (por defecto false), `sort`, `ribbonColor: "smaller" | "larger" | "source"`, `labelOrientation: "radial"`.
347
+
348
+ ### `flowchart` (`flow`, `diagram`)
349
+
350
+ `nodes` con `id`, `label`, `type` (`start`, `end`, `process`, `decision`, `data`, `note`), `lane` opcional (carriles) y `group`; `edges` (o `data`) con `from`, `to` y un `label` opcional ("Sí"/"No"). Diseño por capas con dirección automática (`direction: "TB" | "LR"`), conectores ortogonales con flechas y bucles que rodean por el costado. `nodeWidth`, `swimlanes: false`.
351
+
352
+ ## Cuadrículas, flujos y números
353
+
354
+ ### `heatmap`
355
+
356
+ Categorías `x`, `y` y `value`. Rampa secuencial por defecto; `midpoint` (o `diverging: true`) para una rampa divergente. Los valores se escriben en las celdas cuando caben.
357
+
358
+ ### `sankey`
359
+
360
+ `from`, `to`, `value` (también detecta `source`/`target`). El color parte en la primera etapa con más de un nodo y fluye aguas abajo.
361
+
362
+ ### `wordcloud` (`words`, `tagcloud`, `nube`)
363
+
364
+ Palabras con tamaño según su frecuencia, hechas con cuidado: todas horizontales; el **área** de cada palabra es proporcional a su conteo (el tamaño de la letra crece con la raíz cuadrada); una disposición en espiral determinista desde la palabra más frecuente hacia afuera, sin choques; un solo tono que se oscurece con la frecuencia, o un color por `series` con leyenda. Si las palabras importantes no caben, toda la nube se achica; las palabras que aun así no caben se nombran en el texto alternativo, nunca se cortan.
365
+
366
+ ```js
367
+ plot({ type: 'wordcloud', data, text: 'word', value: 'mentions', title: '“Data” dominates what companies ask for' });
368
+ plot({ type: 'wordcloud', text: openAnswers, title: '…', subtitle: 'Mentions in 312 open answers' }); // raw text
369
+ ```
370
+
371
+ | Opción | Significado |
372
+ |---|---|
373
+ | `text` | Campo con las palabras, o el texto en bruto que se cuenta (un string o una lista de strings). El texto en bruto descarta las palabras vacías en español e inglés y los números. |
374
+ | `textField` | Un campo de texto libre en cada fila (respuestas abiertas de encuestas): las palabras se cuentan entre todas las filas. |
375
+ | `value` | Peso de cada palabra (por defecto: 1 por fila, sumado). |
376
+ | `series` | Color por grupo, con leyenda. |
377
+ | `top` | Máximo de palabras a mostrar (por defecto 80). |
378
+ | `stopwords`, `minLength` | Palabras adicionales a ignorar; largo mínimo de palabra que se conserva (por defecto 3). |
379
+ | `minFont`, `maxFont` | Rango de tamaños de letra. |
380
+ | `highlight` | Palabras a destacar; el resto se atenúa. |
381
+
382
+ Una nube sirve para "qué aparece más" y no para "por cuánto". `lint` sugiere un gráfico de barras (`top: 15`) cuando hay pocas palabras y pide un `subtitle` que diga qué se contó.
383
+
384
+ ### `kpi`
385
+
386
+ `items: [{ label, value, delta, previous, period, good: "up" | "down", trend: [...], target, format }]`. El delta es una fracción (`0.12` → +12%); `good: "down"` para costos. Las tarjetas se reacomodan en filas equilibradas.
387
+
388
+ ## Tablas
389
+
390
+ ### `table` (`tabla`, `datatable`)
391
+
392
+ Para cuando quien lee necesita buscar el número exacto. Líneas finas entre filas y nada más; números alineados a la derecha en cifras tabulares con los mismos decimales en toda la columna; celdas vacías como un guion. Cada columna recibe el indicador que la explica.
393
+
394
+ ```js
395
+ plot({
396
+ type: 'table',
397
+ title: 'Two of six projects are already over budget',
398
+ data: projects,
399
+ columns: ['project', 'area',
400
+ { field: 'progress', type: 'progress' },
401
+ { field: 'spent', label: 'Spent / budget', type: 'progress', target: 'budget', good: 'down' },
402
+ 'status'],
403
+ sortBy: 'progress',
404
+ });
405
+ ```
406
+
407
+ | `type` de columna | Dibuja | Se infiere cuando |
408
+ |---|---|---|
409
+ | `bar` | Valor + barra de datos en una escala compartida (divergente en torno a 0) | Es la columna numérica principal (`value`/`y`, o el primer número); `bars: false` la desactiva |
410
+ | `delta` | ▲ / ▼ + cambio con signo, en verde o rojo según `good` (`"down"` para costos) | El campo se llama var, change, delta, growth, yoy, cambio, variación… |
411
+ | `heat` | Celda teñida con una escala secuencial (divergente cuando cruza el 0); `colorScale` por columna | No se infiere |
412
+ | `sparkline` | Minilínea, con el último punto en el color de acento | El campo contiene listas de números |
413
+ | `progress` | Porcentaje + medidor; contra `target` (campo o número) o como 0–1 / 0–100 | El campo se llama progress, avance, completion…, o meta/target/goal con fracciones |
414
+ | `badge` | Píldora de estado | Los valores son palabras de estado (done, at risk, late, in progress… en es/en/pt/fr/de): colores de bien, advertencia, crítico e información |
415
+ | `check` | ✓ o un guion, con una etiqueta accesible | Booleanos, o sí/no, yes/no… |
416
+ | `date` | Fecha legible en el idioma del gráfico | Fechas ISO |
417
+ | `link` | Enlace subrayado; solo `http(s)` y `mailto` reciben un `href` | URLs |
418
+ | `rank` | Posición según el orden actual | `rank: true` |
419
+ | `text`, `number` | Texto (se divide en 2–3 líneas) y números simples | Todo lo demás |
420
+
421
+ | Opción | Significado |
422
+ |---|---|
423
+ | `columns` | Orden y tipo: nombres de campo o `{ field, label, type, format, align, width, good, target, colorScale, total, lines }`. Omítelo para usar todos los campos. `x`/`label` elige la columna con el nombre de la fila e `y` una lista de columnas de valores. |
424
+ | `sortBy`, `sort` | Columna y dirección (`"desc"` para números, `"asc"` para texto por defecto). `sort: "-sales"` también funciona. Las celdas vacías siempre van al final. |
425
+ | `pageSize`, `page` | Hasta 15 filas se muestran completas; sobre eso, páginas de 10. `pageSize: false` muestra todo. El paginador son solo números y flechas (`11–20 / 248`, `‹ 1 2 3 … 25 ›`). |
426
+ | `query` | Conserva las filas que contienen esas palabras, sin importar tildes ni mayúsculas. |
427
+ | `total` | Fila de totales: suma los números, promedia porcentajes y avances, y deja en blanco los cambios. `{ sales: "mean" }` por columna; `totalLabel` le cambia el nombre. |
428
+ | `group` | Filas bajo una franja teñida por valor, con un conteo. |
429
+ | `highlight` | Las filas cuyo nombre coincide reciben un tinte y una marca de acento; las barras de las demás filas se atenúan. |
430
+ | `top` | Conserva las primeras N filas tras ordenar. |
431
+ | `cards` | `"auto"` (por defecto) convierte cada fila en una tarjeta cuando las columnas no caben (teléfonos): nombre y valor principal arriba, la barra debajo y las demás columnas como líneas etiqueta–valor. `true` siempre, `false` nunca. |
432
+ | `density`, `striped` | Filas `"compact"` / `"comfortable"`; franjas tipo cebra para tablas muy anchas. |
433
+
434
+ En el navegador, `render()` agrega un cuadro de búsqueda sobre 20 filas (`search: false` lo oculta; `true` lo fuerza), encabezados que ordenan con un clic (orden natural, invertido y luego el orden original) y botones de página; todos funcionan desde el teclado y anuncian `aria-sort` y `aria-current`. Al cambiar el tamaño se vuelve a dibujar con el nuevo ancho, así que una tabla se vuelve tarjetas en un teléfono y columnas otra vez en un computador.
435
+
436
+ El idioma de las pocas palabras que escribe una tabla (Total, Buscar, Sin resultados) y el formato de los números vienen de `locale`; sin él, la tabla reconoce el idioma a partir de su propio título, encabezados y categorías.
@@ -0,0 +1,46 @@
1
+ # Escalas de color
2
+
3
+ Todos los mapas de colores que trae matplotlib están disponibles por nombre, además de los tuyos. Úsalos en cualquier gráfico que asigne un color a un número: mapas de calor, coropletas, mapas de puntos, treemaps coloreados por cambio y nubes de palabras.
4
+
5
+ ```js
6
+ plot({ type: 'heatmap', data, x: 'hour', y: 'day', value: 'trips', colorScale: 'viridis' });
7
+ plot({ type: 'choropleth', geo: 'chile', data, key: 'region', value: 'change', colorScale: 'RdBu' });
8
+ plot({ ...spec, colorScale: 'magma_r' }); // _r reverses any map
9
+ plot({ ...spec, colorScale: ['#FFF7EC', '#FC8D59', '#7F0000'] }); // your own stops
10
+ plot({ ...spec, colorScale: { name: 'coolwarm', mid: 100 } }); // diverging around 100
11
+ ```
12
+
13
+ - **Los nombres no distinguen mayúsculas** (`'rdbu'` funciona) y `_r` invierte cualquier mapa.
14
+ - **Los mapas divergentes se centran solos**: en 0 cuando los datos cruzan el cero; si no, en el punto medio de los datos. Fija `mid` (o `midpoint`) para elegir el centro.
15
+ - **Tu propia escala** es una lista de dos o más colores, repartidos de forma pareja de menor a mayor, o `{ colors, reverse, mid }`.
16
+ - La interpolación ocurre en OKLab, así que los pasos se ven parejos. Las paradas se muestrearon de matplotlib 3.9 y quedan a menos de 2 ΔE (×100) del original.
17
+ - `cmap` y `colormap` son alias de `colorScale`. La opción anterior `ramp` todavía funciona.
18
+
19
+ ## Paletas categóricas
20
+
21
+ `palette` acepta una lista de colores, una paleta de plotagents o uno de los mapas cualitativos de matplotlib:
22
+
23
+ ```js
24
+ plot({ ...spec, palette: 'tab10' });
25
+ plot({ ...spec, palette: 'Set2' });
26
+ plot({ ...spec, palette: 'viridis' }); // a continuous map, sampled into distinct colors
27
+ ```
28
+
29
+ La paleta por defecto está validada para deficiencias de visión del color; `lint` revisa cualquier otra paleta que traigas (consulta [Temas](themes.md#validating-a-palette)).
30
+
31
+ ## ¿Cuál usar?
32
+
33
+ | Datos | Usa | Evita |
34
+ |---|---|---|
35
+ | Valores ordenados, de menor → mayor | `viridis`, `cividis`, `magma`, `Blues`, `YlGnBu` | `jet`, `rainbow`, `hsv` (inventan bordes donde los datos no los tienen) |
36
+ | Valores en torno a un centro con significado (0, una meta, un promedio) | `RdBu`, `coolwarm`, `PuOr`, `BrBG` | un mapa secuencial |
37
+ | Ángulos, horas del día, meses | `twilight`, `hsv` | un mapa secuencial |
38
+ | Categorías | `palette: 'tab10'`, `'Set2'`, `'Dark2'` | cualquier mapa continuo como `colorScale` |
39
+
40
+ `lint` marca los mapas arcoíris con una sugerencia y rechaza un mapa cualitativo usado como escala continua.
41
+
42
+ ## Todas las escalas
43
+
44
+ {{colorscales}}
45
+
46
+ La lista también está disponible en código: `colorScaleNames()` devuelve los nombres agrupados por tipo y `resolveColorScale(name)` devuelve `{ name, stops, kind }`.
@@ -0,0 +1,79 @@
1
+ # Dashboards
2
+
3
+ `dashboard(spec)` convierte un puñado de especificaciones de gráficos en un dashboard HTML terminado y autocontenido. Igual que en un gráfico individual, las decisiones de diseño vienen incluidas: tú dices qué va en la página y la biblioteca decide dónde y cómo.
4
+
5
+ ```js
6
+ import { dashboard } from 'plotagents';
7
+ import { writeFileSync } from 'node:fs';
8
+
9
+ writeFileSync('sales.html', dashboard({
10
+ title: 'How are southern sales doing?',
11
+ subtitle: 'Sales by month, region and channel, 2024–2025, CLP millions',
12
+ source: 'Commercial ERP',
13
+ updated: '2026-01-05',
14
+ locale: 'en',
15
+ data: sales, // shared rows
16
+ kpis: [
17
+ { label: 'Sales 2025', field: 'sales', agg: 'sum', format: 'compact',
18
+ compare: { field: 'year', current: 2025, previous: 2024 }, trendBy: 'month' },
19
+ { label: 'Orders 2025', field: 'orders', agg: 'sum', format: 'compact',
20
+ compare: { field: 'year', current: 2025, previous: 2024 } },
21
+ { label: 'Online share', value: 0.361, delta: 0.13, format: 'percent', period: 'vs 2024' },
22
+ ],
23
+ filters: ['region', 'channel'],
24
+ charts: [
25
+ { type: 'line', x: 'month', y: 'sales', series: 'region', highlight: 'Ñuble',
26
+ title: 'Ñuble grew 37% this year, faster than any other region' },
27
+ { type: 'choropleth', geo: 'chile', key: 'region', value: 'sales', fitTo: ['Biobío', 'Ñuble', 'Maule', 'Araucanía'],
28
+ title: 'Biobío makes 36% of sales' },
29
+ { type: 'bar', x: 'channel', y: 'sales', series: 'year',
30
+ title: 'Online is the only channel growing strongly' },
31
+ { type: 'treemap', path: ['region', 'channel'], value: 'sales',
32
+ title: 'Physical stores are still half the business' },
33
+ ],
34
+ }));
35
+ ```
36
+
37
+ La página funciona sin JavaScript: cada gráfico se dibuja en el servidor, así que se abre en cualquier navegador, se imprime y se convierte a PDF. Cuando la versión para navegador está disponible (lo está, en Node), la página también recibe tooltips, ampliación de mapas y filtros en vivo. En una aplicación web, `renderDashboard('#board', spec)` dibuja el mismo dashboard dentro de un elemento.
38
+
39
+ ## Lo que decide por ti
40
+
41
+ Estos valores por defecto vienen de la práctica del diseño de dashboards: *Information Dashboard Design* de Stephen Few, el patrón de lectura en Z y lo que hace difíciles de leer los dashboards de negocio.
42
+
43
+ | Regla | Lo que hace plotagents |
44
+ |---|---|
45
+ | **Una pregunta por dashboard** | El `title` es la pregunta ("¿Cómo van las ventas del sur?"). `lintDashboard` advierte sobre 9 gráficos y sugiere dividir sobre 6. |
46
+ | **Primero los números** | Los KPI van en una fila arriba, como máximo 5 (advertencia sobre eso). Cada uno tiene una etiqueta, la cifra, su cambio con una flecha **y** un signo (nunca solo color), el período de comparación y una minilínea opcional. |
47
+ | **Lectura en Z** | El gráfico principal va justo después de los KPI, a todo el ancho: el marcado con `primary: true` o, si no, la primera serie de tiempo. |
48
+ | **Filas completas, sin huecos** | Una cuadrícula de 12 columnas en filas equilibradas: 2 gráficos → 6 + 6 (8 + 4 cuando uno es ancho), 3 → 4 + 4 + 4, 4 → dos filas de dos, 5 → 3 + 2. Los paneles de una fila comparten la altura. |
49
+ | **El ancho sigue al gráfico** | Las series de tiempo, las cartas Gantt, los mapas de calor y los flujos se leen de izquierda a derecha, así que reciben paneles anchos. Las donas, los KPI y los rankings cortos reciben paneles angostos. El mapa de Chile recibe una columna angosta. |
50
+ | **Lo mismo, del mismo color** | Un solo mapa de colores para todos los gráficos: "Ñuble" tiene el mismo color en la línea, el treemap y las barras. `colors` fija colores para toda la página. |
51
+ | **Una sola voz** | Un tema, un idioma y una tipografía para todos los paneles. La fuente aparece una vez, abajo; un gráfico conserva su propia fuente solo cuando es distinta. |
52
+ | **Filtros como botones** | `filters` se convierten en botones cuando un campo tiene hasta 6 valores, y en una lista desplegable cuando tiene más. Filtran todos los gráficos que tienen el campo, recalculan los KPI y dicen qué está filtrado en cada subtítulo. Un filtro sobre el campo `time` de un gráfico lleva ese gráfico al período elegido. |
53
+ | **Teléfonos** | Una columna en orden de lectura; los KPI en dos columnas. |
54
+ | **Impresión** | Los paneles no se cortan entre páginas; la barra de filtros no queda fija en papel. |
55
+
56
+ ## Especificación
57
+
58
+ | Opción | Significado |
59
+ |---|---|
60
+ | `title`, `subtitle` | La pregunta y el alcance (medida, unidad, período). |
61
+ | `source`, `updated`, `note` | Metadatos del pie y del encabezado. |
62
+ | `locale`, `theme`, `font`, `palette` | Se aplican a todos los gráficos (`theme: "noche"` para un dashboard oscuro). |
63
+ | `data` | Filas compartidas. Los gráficos sin `data` las usan; los KPI calculados y los filtros trabajan sobre ellas. |
64
+ | `kpis` | `[{ label, value, delta, period, trend, good, format }]` (fijos) o `[{ label, field, agg, over, scale, compare, trendBy, format }]` (calculados). `agg`: `sum`, `mean`, `median`, `min`, `max`, `count`, `distinct`, `ratio` (con `over`). `compare: { field, current, previous }` muestra el período actual y su cambio. `trendBy: "month"` dibuja la minilínea. |
65
+ | `charts` | Especificaciones de gráficos, como en `plot()`. Por gráfico: `primary: true`, `size: "full" \| "large" \| "medium" \| "small"` o `span` (de 12). |
66
+ | `filters` | `["region"]` o `[{ field, label, control: "buttons" \| "select", value }]`. |
67
+ | `layout` | `"auto"` (por defecto), `"executive"` (gráfico principal a todo el ancho y luego pares) o `"grid"` (paneles iguales). |
68
+ | `colors` | `{ "Biobío": "#3A47B8" }` para todos los gráficos. |
69
+
70
+ ## Para agentes
71
+
72
+ ```js
73
+ import { safeDashboard, toolDefinition } from 'plotagents';
74
+
75
+ const tool = toolDefinition({ kind: 'dashboard' }); // JSON Schema of the dashboard spec
76
+ const { ok, html, errors, warnings, suggestions } = safeDashboard(input);
77
+ ```
78
+
79
+ `safeDashboard` nunca lanza errores. Revisa el dashboard como un todo (demasiados gráficos o KPI, varios gráficos de torta, temas mezclados, filtros sobre campos que ningún gráfico tiene, paneles sin título) y cada gráfico que contiene, y etiqueta cada problema con el número y el título del gráfico para que el modelo sepa cuál corregir. `planDashboard(spec)` devuelve las decisiones de diseño (paneles, anchos, filas, colores, filtros) sin dibujar nada.