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
package/docs/charts.md CHANGED
@@ -15,7 +15,7 @@ Every chart takes a spec with `data` (rows) plus field names for each role. The
15
15
  | `highlight` | One name or a list: emphasize these, mute the rest. |
16
16
  | `colors` | `{ "name": "#hex" }` to pin an entity's color. |
17
17
  | `format`, `xFormat`, `yFormat` | `"number" \| "compact" \| "percent" \| "currency" \| "integer"` or `{ prefix, suffix, decimals, currency }`. |
18
- | `locale` | `"es"` (default: `1.234,5`) or `"en"` (`1,234.5`). |
18
+ | `locale` | `"es"` (default: `1.234,5`), `"en"` (`1,234.5`), `"pt"`, `"fr"` (`1 234,5`), `"de"`, or `"auto"`: recognized from the title, subtitle, column names and categories. Tables use `"auto"` when `locale` is omitted. |
19
19
  | `xLabel`, `yLabel` | Axis titles (horizontal, never rotated). |
20
20
  | `xScale`, `yScale` | `"log"` where it makes sense. |
21
21
  | `xDomain`, `yDomain` | Fixed `[min, max]`. |
@@ -134,6 +134,40 @@ plot({
134
134
  | `kind` | `jointplot`: `"scatter"` or `"reg"`. |
135
135
  | `marginal` | `jointplot`: `"hist"` or `"kde"`. |
136
136
 
137
+ ### `corr` (`correlation`, `corrplot`) · `clustermap`
138
+
139
+ Correlation matrix of numeric fields, computed for you from the rows (pairwise complete cases, like `df.corr()`). Each pair appears once, below the diagonal, on a diverging scale fixed at −1 to 1, so the same color means the same strength in every chart. Long labels turn 45°. The tooltip gives r, n and the p-value.
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
+ | Option | Meaning |
147
+ |---|---|
148
+ | `vars` | Fields to relate, in order. Default: every numeric field. `y: [...]` works too. |
149
+ | `method` | `"pearson"` (default) or `"spearman"` (ranks: monotonic relations, robust to outliers). |
150
+ | `triangle` | `"lower"` (default) or `"full"`. |
151
+ | `cluster` | Reorder by hierarchical clustering (average linkage on 1 − r) and draw the dendrogram on top. `type: "clustermap"` sets it and shows the full matrix; `dendrogram: false` keeps the order without the tree. |
152
+ | `alpha` | Grey out pairs with p ≥ alpha (Fisher's z) and add a legend key. |
153
+ | `order` | Variable order (list). |
154
+ | `highlight` | One variable: its row and column stay, the rest fade. |
155
+ | `labelsFor` | Display names, e.g. `{ areas_verdes: "Green space" }`. |
156
+ | `x`, `y`, `value` | Draw a matrix you already computed, in long form (one row per pair). |
157
+
158
+ ### `pairplot` (`pairs`, `splom`, `scattermatrix`)
159
+
160
+ Scatter-plot matrix: each pair as a scatter below the diagonal, each variable's distribution on it (histogram, or density with `hue`), and Pearson r above, tinted by strength and split by `hue` group when there is room. Axes are shared by column and row; tick labels sit only on the outer edges.
161
+
162
+ | Option | Meaning |
163
+ |---|---|
164
+ | `vars` | Fields to plot (3–6 read best). Default: the first 6 numeric fields. |
165
+ | `hue` | Color by group, with a legend. |
166
+ | `diag` | `"hist"` or `"kde"`. |
167
+ | `upper` | `"corr"` (default), `"scatter"` or `false`. `corner: true` keeps only the lower triangle. |
168
+ | `kind` | `"reg"` adds a least-squares line to every scatter panel. |
169
+ | `radius`, `bins`, `panelSize` | Point radius, histogram bins, maximum panel size. |
170
+
137
171
  ### `radar` (`spider`)
138
172
 
139
173
  Long data (`axis`, `value`, `series`) or wide data (`axes: [fields]`, one row per series with `label`). `normalize` scales each axis to its own maximum (tooltips keep raw values); `grid: "polygon" | "circle"`; `domain`. Best with ≤ 4 series.
@@ -350,3 +384,54 @@ A cloud is good at "what comes up most" and bad at "by how much". `lint` suggest
350
384
  ### `kpi`
351
385
 
352
386
  `items: [{ label, value, delta, previous, period, good: "up" | "down", trend: [...], target, format }]`. Delta is a fraction (`0.12` → +12%); `good: "down"` for costs. Tiles reflow into balanced rows.
387
+
388
+ ## Tables
389
+
390
+ ### `table` (`tabla`, `datatable`)
391
+
392
+ For when the reader needs to look up the exact number. Thin rules between rows and nothing else; numbers right-aligned in tabular figures with the same decimals down each column; empty cells as a dash. Each column gets the indicator that explains it.
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
+ | Column `type` | Draws | Inferred when |
408
+ |---|---|---|
409
+ | `bar` | Value + data bar on a shared scale (diverging around 0) | The main numeric column (`value`/`y`, or the first number); `bars: false` turns it off |
410
+ | `delta` | ▲ / ▼ + signed change, green or red by `good` (`"down"` for costs) | Field named var, change, delta, growth, yoy, cambio, variación… |
411
+ | `heat` | Cell tinted by a sequential scale (diverging when it crosses 0); `colorScale` per column | — |
412
+ | `sparkline` | Mini line, last point in the accent | The field holds lists of numbers |
413
+ | `progress` | Percent + meter; against `target` (field or number) or as 0–1 / 0–100 | Field named progress, avance, completion…, or meta/target/goal with fractions |
414
+ | `badge` | Status pill | Values are status words (done, at risk, late, in progress… in es/en/pt/fr/de): good, warning, critical, info colors |
415
+ | `check` | ✓ or a dash, with an accessible label | Booleans, or sí/no, yes/no… |
416
+ | `date` | Readable date in the chart's language | ISO dates |
417
+ | `link` | Underlined link; only `http(s)` and `mailto` get an `href` | URLs |
418
+ | `rank` | Position under the current sort | `rank: true` |
419
+ | `text`, `number` | Text (wraps to 2–3 lines) and plain numbers | Everything else |
420
+
421
+ | Option | Meaning |
422
+ |---|---|
423
+ | `columns` | Order and type: field names or `{ field, label, type, format, align, width, good, target, colorScale, total, lines }`. Omit to use every field. `x`/`label` picks the row-name column and `y` a list of value columns. |
424
+ | `sortBy`, `sort` | Column and direction (`"desc"` for numbers, `"asc"` for text by default). `sort: "-sales"` works too. Empty cells always go last. |
425
+ | `pageSize`, `page` | Up to 15 rows are shown whole; beyond that, pages of 10. `pageSize: false` shows everything. The pager is numbers and arrows only (`11–20 / 248`, `‹ 1 2 3 … 25 ›`). |
426
+ | `query` | Keep rows that contain these words, ignoring accents and case. |
427
+ | `total` | Totals row: sums numbers, averages percents and progress, leaves changes blank. `{ sales: "mean" }` per column; `totalLabel` renames it. |
428
+ | `group` | Rows under a tinted band per value, with a count. |
429
+ | `highlight` | Rows whose name matches get a tint and an accent mark; the bars of the other rows are muted. |
430
+ | `top` | Keep the first N rows after sorting. |
431
+ | `cards` | `"auto"` (default) turns each row into a card when the columns do not fit (phones): name and main value on top, the bar under them, the other columns as label–value lines. `true` always, `false` never. |
432
+ | `density`, `striped` | `"compact"` / `"comfortable"` rows; zebra stripes for very wide tables. |
433
+
434
+ In the browser `render()` adds a search box above 20 rows (`search: false` hides it; `true` forces it), click-to-sort headers (natural order, reversed, then the original order), and page buttons; all of them work from the keyboard and announce `aria-sort` and `aria-current`. Resizing re-renders at the new width, so a table becomes cards on a phone and columns again on a desktop.
435
+
436
+ The language of the few words a table writes (Total, Search, No results) and the number format come from `locale`; without it, the table recognizes the language from its own title, headers and categories.
437
+
@@ -0,0 +1,102 @@
1
+ # plotagents · guía para agentes de IA
2
+
3
+ Una función, un objeto JSON. Sin decisiones de diseño: la biblioteca las toma por ti.
4
+
5
+ ```js
6
+ import { plot, safePlot } from 'plotagents';
7
+ const svg = plot({ type, data, x, y, series, title, subtitle, source });
8
+ ```
9
+
10
+ ## Reglas para un buen gráfico
11
+
12
+ 1. **`title` es el hallazgo**, una oración completa con la cifra clave: "Biobío concentra el 30% de las ventas", no "Ventas por región".
13
+ 2. **`subtitle` dice cómo leerlo**: medida, unidad y período.
14
+ 3. **Define siempre `source`.** Si los datos son inventados, usa `source: "illustrative data"`.
15
+ 4. `data` es una lista de filas (`[{ region: "Biobío", sales: 1240 }]`). Para una tabla ancha, pasa `y: ["revenue", "costs"]`.
16
+ 5. Usa `safePlot(spec)` como tu herramienta: nunca lanza excepciones y devuelve `errors`, `warnings` y `suggestions`, cada uno con un `fix`. Si `ok` es `false`, corrige el spec y vuelve a llamarla.
17
+ 6. Define `locale: "en"` para formatos numéricos en inglés (el valor por defecto es español: `1.234,5`); también hay `"pt"`, `"fr"`, `"de"` o `"auto"` para reconocerlo a partir del texto. Las tablas lo reconocen por sí solas.
18
+
19
+ ## Qué tipo usar
20
+
21
+ | Para mostrar | `type` | Campos clave |
22
+ |---|---|---|
23
+ | Comparar categorías | `bar` | `x`, `y`, `series?`, `stack?` (`true` / `"percent"`), `top?` |
24
+ | Contar filas por categoría | `countplot` | `x` |
25
+ | Media por categoría con un IC | `barplot` o `point` | `x`, `y`, `hue?` |
26
+ | Cambio en el tiempo | `line` | `x` fecha (`"2024-03"`) o año, `y`, `series?`; `ci: true` con varias observaciones por x |
27
+ | Un plan de proyecto o un cronograma | `gantt` | `task`, `start`, `end` (o `duration`), `group?`, `progress?`, `dependsOn?`, `today?`, `collapsed?` |
28
+ | Volumen en el tiempo | `area` | como `line`; se apila con `series` |
29
+ | Medidas en escalas distintas | `line` + `index: true`, o `facet` | **no hay doble eje** |
30
+ | Crecimiento exponencial | `line` + `yScale: "log"` | |
31
+ | Relación entre dos medidas | `regplot` (ajuste + IC) / `scatter` / `bubble` | `x`, `y`, `hue?`, `size?`, `label?` |
32
+ | Relación + marginales | `jointplot` | `x`, `y` |
33
+ | Qué medidas se mueven juntas | `corr` (`correlation`) | `vars?` (por defecto: todos los campos numéricos), `method?` (`"spearman"`), `alpha?: 0.05`; `cluster: true` o `type: "clustermap"` las agrupa con un dendrograma |
34
+ | Todos los pares de medidas a la vez | `pairplot` (`splom`) | `vars` (3–6), `hue?`, `corner?`, `kind?: "reg"` |
35
+ | Distribución de una medida | `hist` (`kde: true`), `kde`, `ecdf` | `x`, `hue?` |
36
+ | Distribución por categoría | `box`, `violin`, `strip`, `swarm` | `x` categoría, `y` medida, `hue?` |
37
+ | Perfil multidimensional | `radar` | `axis`, `value`, `series` (≤ 4 series) |
38
+ | Parte de un todo jerárquico | `treemap` o `sunburst` | `path: [level1, level2, …]`, `value` |
39
+ | Parte de un todo, ≤ 6 partes | `donut` / `pie` | `label`, `value`; `half: true` para un semicírculo |
40
+ | Magnitud en una grilla | `heatmap` | `x`, `y`, `value` |
41
+ | Un valor por región, estado o país | `choropleth` (`symbolmap` para totales) | `geo: "chile" \| "usa" \| "world"` o cualquier objeto GeoJSON/TopoJSON, `key`, `value` (texto → un color por categoría) |
42
+ | Puntos en lugares reales sobre un mapa de calles | `pointmap` | `lon`, `lat` (se detectan solos), `size?`, `hue?`, `value?`, `label?`; `tiles: "osm"` (por defecto) / `"esri-gray"` / `"esri-dark"`, o carto-* / mapbox-* con `tilesKey` |
43
+ | Quién se conecta con quién | `network` | aristas `from`, `to`, `value?`; `nodes?` con `group`; `layout?` |
44
+ | Intercambios entre pocos grupos | `chord` | `from`, `to`, `value` |
45
+ | Estructura por edad | `pyramid` | `age`, `sex`, `value` |
46
+ | Un proceso con decisiones | `flowchart` | `nodes` (`type`, `lane`), `edges` (`label`) |
47
+ | Flujos entre etapas | `sankey` | `from`, `to`, `value` |
48
+ | Una o más cifras | `kpi` | `items: [{ label, value, delta, period, trend, target, good }]` |
49
+ | Valores exactos para consultar | `table` | `columns?: [field \| { field, type, label, format, good, target }]`, `sortBy?`, `total?`, `group?`; los tipos `bar`, `delta`, `heat`, `sparkline`, `progress`, `badge` y `check` se infieren cuando se omiten |
50
+ | Lo que más aparece en un texto | `wordcloud` | `text` (campo o texto sin procesar), `value?`, `series?`; para comparar con exactitud, prefiere `bar` + `top` |
51
+
52
+ Los nombres de seaborn funcionan (`histplot`, `kdeplot`, `boxplot`, `violinplot`, `lmplot`, `stripplot`, `swarmplot`, `pointplot`), y `hue` es un alias de `series`. Omite `type` para que se infiera de los datos.
53
+
54
+ ## Opciones más útiles
55
+
56
+ - `highlight: "Ñuble"`: destaca una categoría o serie y atenúa el resto. `exclude: ["Maule"]` quita entradas y recalcula el gráfico; los colores no cambian.
57
+ - `legend: "top" | "bottom" | "left" | "right" | false`: dónde va la leyenda (arriba por defecto).
58
+ - `colorScale: "viridis"`: cualquier mapa de colores de matplotlib para mapas de calor, mapas y treemaps por variación (`"RdBu"` diverge en torno a 0; `_r` lo invierte; también sirve una lista de colores). `palette: "tab10"` para categorías.
59
+ - `dashed` para lo que no es una cifra firme (estimaciones, proyecciones, cifras provisionales): un nombre, una lista o un campo de los datos (`dashed: "provisional"`). Las formas mantienen su color, con relleno achurado y contorno discontinuo. En un eje de tiempo, `dashedFrom: 2025` dibuja con trazo discontinuo las líneas, áreas y columnas desde ese año. **Define siempre `dashedLabel`** ("Proyección", "Preliminar") para que los lectores sepan qué significa el achurado.
60
+ - `facet: "region"` (+ `columns`): un panel por valor, con una escala compartida.
61
+ - `time: "year"`: un período a la vez (el más reciente) con la variación % respecto del anterior. Los controles de tiempo están **desactivados por defecto**: agrega `slider: true` solo cuando la persona pida recorrer los períodos (botones para ≤ 8 períodos y un deslizador mínimo en los demás casos; `"buttons"` / `"slider"` fuerzan uno de los dos, `play: false` oculta el botón de reproducir). Los treemaps se pueden colorear por variación con `colorBy: "change"`.
62
+ - `refLines: [{ y: 600, label: "Target" }]` y `annotations: [{ x: "2024-01", label: "New branch" }]`.
63
+ - `format: "percent" | "currency" | "compact" | "integer"` o `{ prefix, suffix, decimals }`.
64
+ - `xScale` / `yScale: "log"`.
65
+ - `table`: dale un título que enuncie el hallazgo, como a cualquier gráfico. Con más de 15 filas se pagina de a diez (`pageSize`), en teléfonos cada fila se muestra como una tarjeta y el navegador agrega orden, búsqueda y paginación. Usa `good: "down"` en los costos para que ▲ se vuelva rojo, `progress` con `target: "budget"` para el gasto frente al presupuesto y `total: true` para una fila de totales. Usa entre 4 y 7 columnas.
66
+ - `theme: "noche"` o un objeto JSON (`{ extends, categorical, type, marks, font }`); `palette: [...]` para tus propios colores.
67
+ - `font: "Poppins"` y `textScale: 1.5` (modo presentación); `configure({ font, textScale })` los define para todos los gráficos.
68
+ - `network` + `focus: "node id"`: centra un nodo y destaca sus vecinos.
69
+ - Tu propio mapa: `geo: <GeoJSON or TopoJSON>`; la propiedad que coincide se busca sola (`featureKey` para forzarla). Agrega `markers: [{ lon, lat, label }]`, `layers: [{ geo, label }]` para ríos o rutas, y `fitTo: ["Biobío"]` o una caja `[lonMin, latMin, lonMax, latMax]` para hacer zoom.
70
+ - `gantt`: las fechas `end` por día son inclusivas; una fila sin `end` es un hito. Define siempre `today` en un plan en curso. `collapsed: ["Phase 2"]` (o `true`) pliega las fases en una barra resumen; en el navegador, un clic en una fase la abre o la cierra.
71
+ - `network` + `layout: "force" | "circular" | "radial" | "concentric" | "clusters" | "tree" | "grid"` elige la disposición; `curveness: 0` endereza las aristas.
72
+ - `pointmap` dibuja sobre teselas de OpenStreetMap por defecto (`tiles: "esri-gray"`, `"mapbox-light"` + `tilesKey`, o tus propias `{ url, attribution }`). Las teselas solo se cargan cuando el SVG va incrustado en la página o en `toHTML`, no dentro de `<img>`.
73
+ - Tu propio estilo para todos los gráficos: `registerTheme("acme", { background, text, grid: { color, dash }, categorical: "tab10", font })` y luego `configure({ theme: "acme" })`.
74
+ - `geo: "usa"`: los estados coinciden por código postal ("TX"), nombre en inglés o en español, o código FIPS ("48"). Alaska y Hawái aparecen en recuadros; Puerto Rico aparece solo cuando tus datos lo incluyen. En el navegador, los mapas hacen zoom con un clic, con Ctrl/⌘ + rueda o pellizcando.
75
+
76
+ ## Errores que la biblioteca ya resuelve
77
+
78
+ - Más de 8 series → de la 9.ª en adelante se pintan en gris; `lint` sugiere `highlight` o `facet`.
79
+ - Etiquetas de líneas que chocan → se separan con líneas guía.
80
+ - Dona con más de 6 partes → el resto se agrupa en "Otros".
81
+ - Etiquetas de categoría largas → el gráfico pasa a horizontal.
82
+ - Paleta propia difícil de distinguir → `lint` nombra el par problemático.
83
+ - Números como texto ("1.234,5", "12%", "$ 3.000") → se interpretan automáticamente.
84
+ - Decenas de miles de puntos → se dibujan como trazados agrupados y sigue siendo rápido.
85
+
86
+ ## Tableros
87
+
88
+ Cuando alguien pida un dashboard, llama a `safeDashboard({ title, subtitle, source, data, kpis, charts, filters })` (herramienta: `toolDefinition({ kind: 'dashboard' })`). La disposición, los anchos, los colores compartidos y los filtros se deciden por ti:
89
+
90
+ - `title` es la **pregunta** que responde el tablero; cada gráfico igual lleva su hallazgo como título.
91
+ - Como máximo **5 KPI**, calculados a partir de `data` cuando sea posible: `{ label, field, agg: "sum", compare: { field: "year", current: 2025, previous: 2024 }, trendBy: "month" }`.
92
+ - **De 4 a 6 gráficos**. La primera serie de tiempo (o la que tenga `primary: true`) va a todo el ancho; el resto llena filas equilibradas.
93
+ - `filters: ["region", "channel"]` se convierten en botones que filtran todos los gráficos y KPI.
94
+ - No más de una dona; una sola `source` para la página.
95
+
96
+ ## Salidas
97
+
98
+ - `plot(spec)` → cadena SVG. Insértala en HTML, Markdown, un archivo `.svg` o un PDF.
99
+ - `toHTML([spec1, spec2])` → página autocontenida con tooltips, deslizadores y drill-down.
100
+ - `describe(spec)` → texto alternativo del gráfico.
101
+ - `schema` / `toolDefinition()` → JSON Schema y definición de herramienta (Anthropic u OpenAI).
102
+ - CLI: `npx plotagents spec.json -o chart.svg`.
package/docs/es/api.md ADDED
@@ -0,0 +1,172 @@
1
+ # Referencia de la API
2
+
3
+ ```js
4
+ import {
5
+ plot, safePlot, lint, describe, render, toHTML, configure,
6
+ dashboard, safeDashboard, renderDashboard, lintDashboard, planDashboard,
7
+ schema, dashboardSchema, toolDefinition, validatePalette, registerGeo, countWords,
8
+ } from 'plotagents';
9
+ ```
10
+
11
+ Los tipos completos están en [`index.d.ts`](../index.d.ts) y cada opción del spec, en [`schema.json`](../schema.json).
12
+
13
+ ## Dibujo
14
+
15
+ ### `plot(spec)`
16
+
17
+ Dibuja un gráfico y devuelve una cadena SVG. Funciona en Node, Deno, Bun y el navegador. Lanza un `PlotError` cuando el spec no se puede dibujar; el `hint` del error dice cómo corregirlo.
18
+
19
+ ```js
20
+ const svg = plot({ type: 'bar', data, x: 'region', y: 'sales', title: 'Biobío leads with 30%' });
21
+ ```
22
+
23
+ ### `safePlot(spec)`
24
+
25
+ La versión que debes exponer como herramienta de un agente: nunca lanza excepciones.
26
+
27
+ ```js
28
+ const { ok, svg, type, description, errors, warnings, suggestions } = safePlot(spec);
29
+ ```
30
+
31
+ | Campo | Significado |
32
+ |---|---|
33
+ | `ok` | `true` cuando el gráfico se dibujó. |
34
+ | `svg` | La cadena SVG, o `null`. |
35
+ | `type` | El tipo de gráfico resuelto (útil cuando `type` se infirió). |
36
+ | `description` | Texto alternativo del gráfico. |
37
+ | `errors` | Problemas que impiden dibujar el gráfico: `{ code, message, fix }`. |
38
+ | `warnings` | Problemas que vuelven el gráfico engañoso o difícil de leer. |
39
+ | `suggestions` | Mejoras de diseño, como un mejor título o `highlight`. |
40
+
41
+ ### `lint(spec)`
42
+
43
+ Revisa un spec sin dibujarlo y devuelve `{ ok, type, errors, warnings, suggestions }`. Los mensajes siguen el `locale` del spec (español por defecto, inglés con `locale: "en"`); cada uno trae un `fix` con el cambio que hay que hacer.
44
+
45
+ ### `dashboard(spec)` · `safeDashboard(spec)` · `renderDashboard(el, spec)`
46
+
47
+ Un dashboard HTML autocontenido con una disposición según buenas prácticas, colores compartidos, KPI y filtros. `safeDashboard` nunca lanza excepciones y devuelve `{ ok, html, plan, errors, warnings, suggestions }`; `renderDashboard` dibuja dentro de un elemento en el navegador; `lintDashboard` revisa; `planDashboard` devuelve las decisiones de disposición. Consulta [Tableros](dashboards.md).
48
+
49
+ ### `describe(spec)`
50
+
51
+ Devuelve una descripción en texto del gráfico, como "Mayor: Biobío con 1.240 (30%)…". El mismo texto va en el `<desc>` del SVG para los lectores de pantalla.
52
+
53
+ ## En el navegador
54
+
55
+ ### `render(target, spec, opts?)`
56
+
57
+ Dibuja dentro de un elemento (un selector CSS o un nodo del DOM) con tooltips, ancho adaptable, controles de tiempo cuando el spec los pide (`slider: true`), drill-down en treemaps y sunbursts, clic para enfocar en redes, fases plegables en Gantt y clics en la leyenda (shift + clic excluye una entrada; en treemaps y sunbursts, un clic hace zoom en la categoría).
58
+
59
+ ```js
60
+ const chart = plotagents.render('#chart', spec);
61
+ chart.update({ ...spec, highlight: 'Ñuble' }); // redraw
62
+ chart.destroy(); // remove listeners and markup
63
+ ```
64
+
65
+ | Opción | Significado |
66
+ |---|---|
67
+ | `controls` | `false` oculta los controles de tiempo aunque el spec los pida (`slider: true`). |
68
+
69
+ El controlador expone `svg` (el elemento `<svg>` actual) y `spec`.
70
+
71
+ ### `attach(root)`
72
+
73
+ Agrega tooltips a un contenedor que ya tiene un SVG de plotagents, por ejemplo uno generado en el servidor.
74
+
75
+ ### `toHTML(specs, opts?)`
76
+
77
+ Una página HTML autocontenida con uno o más gráficos interactivos y la biblioteca incluida.
78
+
79
+ | Opción | Por defecto | Significado |
80
+ |---|---|---|
81
+ | `title` | `"Gráficos"` | Título de la página. |
82
+ | `lang` | `"es"` | Idioma de la página. |
83
+ | `columns` | `1` | Con dos o más, los gráficos se ordenan en una grilla. |
84
+
85
+ ## Configuración
86
+
87
+ ### `configure(options)`
88
+
89
+ Define valores por defecto para todos los gráficos siguientes; el spec siempre tiene prioridad.
90
+
91
+ ```js
92
+ configure({ font: 'Poppins', textScale: 1.5, locale: 'en' });
93
+ configure(); // returns the current defaults
94
+ configure(null); // clears them
95
+ ```
96
+
97
+ Acepta cualquier opción del spec: `font`, `textScale`, `locale`, `theme`, `palette`, `ramp`, `colorScale`, `legend`, etc. Si un spec trae su propio objeto `theme`, este se combina sobre el global.
98
+
99
+ ### `registerTheme(name, theme)`
100
+
101
+ Registra un tema con nombre (combinado sobre `extends`, `papel` por defecto) para que `theme: name` y `configure({ theme: name })` puedan usarlo. Consulta [Temas](themes.md#your-own-theme-everywhere).
102
+
103
+ ```js
104
+ registerTheme('acme', { background: '#FFF', text: '#111', categorical: 'tab10', grid: { dash: '2 3' } });
105
+ configure({ theme: 'acme' });
106
+ ```
107
+
108
+ ### `colorScaleNames()` · `resolveColorScale(name)`
109
+
110
+ Todos los nombres de escalas de color agrupados por tipo (`sequential`, `diverging`, `cyclic`, `qualitative`), y las paradas de una escala: `{ name, stops, kind }`. `interpolateColor(stops, t)` devuelve el color en `t ∈ [0, 1]` (OKLab).
111
+
112
+ ### `validatePalette(colors, opts?)`
113
+
114
+ Revisa una paleta propia con las mismas pruebas que pasa la paleta incluida. Consulta [temas](themes.md#validating-a-palette).
115
+
116
+ ```js
117
+ validatePalette(['#223C6A', '#E69B0A', '#D21428'], { mode: 'light', surface: '#FFFFFF' });
118
+ // → { ok: false, checks: [{ name: 'lightness', status: 'fail', detail: '…' }, …] }
119
+ ```
120
+
121
+ ### `registerGeo(name, featureCollection, aliases?)`
122
+
123
+ Registra tu propio GeoJSON para que los specs puedan usar `geo: name`. Chile y el mundo vienen incluidos.
124
+
125
+ ## Para agentes
126
+
127
+ ### `toolDefinition({ name?, flavor? })`
128
+
129
+ Devuelve una definición de herramienta construida a partir del JSON Schema: `flavor: "anthropic"` (por defecto) entrega `{ name, description, input_schema }` y `flavor: "openai"` entrega `{ type: "function", function: { … } }`.
130
+
131
+ ```js
132
+ import Anthropic from '@anthropic-ai/sdk';
133
+ import { safePlot, toolDefinition } from 'plotagents';
134
+
135
+ const client = new Anthropic();
136
+ const res = await client.messages.create({
137
+ model: process.env.ANTHROPIC_MODEL,
138
+ max_tokens: 4096,
139
+ tools: [toolDefinition()],
140
+ messages: [{ role: 'user', content: 'Plot the distribution of service times per clinic: …' }],
141
+ });
142
+ const call = res.content.find((b) => b.type === 'tool_use');
143
+ const { ok, svg, errors } = safePlot(call.input);
144
+ // If !ok, send `errors` back as the tool_result: every error carries a `fix`.
145
+ ```
146
+
147
+ ### `schema`
148
+
149
+ El JSON Schema de un spec, publicado también como [`schema.json`](../schema.json) e importable como `plotagents/schema.json`.
150
+
151
+ ## Utilidades
152
+
153
+ | Exportación | Significado |
154
+ |---|---|
155
+ | `TYPES` | Todos los nombres de tipos de gráfico. |
156
+ | `themes`, `getTheme(t)`, `themeFor(spec)` | Los temas incluidos y el tema resuelto de un spec. |
157
+ | `palettes`, `ramps` | Paletas categóricas con nombre y rampas secuenciales y divergentes. |
158
+ | `timeValues(spec)` | Valores ordenados del campo `time`. |
159
+ | `makeFormat(fmt, locale, values)` | El formateador de números que usaría un gráfico. |
160
+ | `formatNumber(v, decimals, locale)`, `formatCompact(v, locale)` | Formato de números según el idioma. |
161
+ | `squarify(items, x, y, w, h)` | El algoritmo de disposición del treemap. |
162
+ | `bundleSource()` | Solo en Node: la versión para navegador como cadena, para incrustarla. |
163
+
164
+ ## Puntos de entrada del paquete
165
+
166
+ | Importación | Contenido |
167
+ |---|---|
168
+ | `plotagents` | Todo lo anterior (módulo ES). |
169
+ | `plotagents/schema.json` | El JSON Schema. |
170
+ | `plotagents/browser` | `dist/plotagents.js`: un `<script>` que define `window.plotagents`. |
171
+ | `plotagents/browser/geo` | `dist/plotagents-geo.js`: geometrías de Chile y del mundo para la versión de navegador. |
172
+ | `plotagents/geo/chile`, `plotagents/geo/world` | Las geometrías como módulos ES. |