plotagents 0.5.0 → 0.7.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 (51) hide show
  1. package/AGENTS.md +24 -4
  2. package/README.md +26 -14
  3. package/bin/plotagents.js +38 -14
  4. package/dist/plotagents-geo.js +1 -1
  5. package/dist/plotagents.js +4294 -637
  6. package/docs/api.md +23 -5
  7. package/docs/charts.md +143 -9
  8. package/docs/colorscales.md +46 -0
  9. package/docs/dashboards.md +79 -0
  10. package/docs/getting-started.md +6 -2
  11. package/docs/themes.md +39 -9
  12. package/index.d.ts +188 -17
  13. package/package.json +9 -3
  14. package/schema.json +1025 -46
  15. package/src/axes.js +11 -3
  16. package/src/charts/bar.js +28 -17
  17. package/src/charts/categorical.js +31 -7
  18. package/src/charts/chord.js +3 -3
  19. package/src/charts/choropleth.js +284 -36
  20. package/src/charts/dist.js +37 -20
  21. package/src/charts/donut.js +1 -1
  22. package/src/charts/gantt.js +460 -0
  23. package/src/charts/heatmap.js +2 -2
  24. package/src/charts/kpi.js +9 -6
  25. package/src/charts/line.js +173 -41
  26. package/src/charts/network.js +552 -189
  27. package/src/charts/pointmap.js +599 -0
  28. package/src/charts/radar.js +7 -1
  29. package/src/charts/regplot.js +7 -1
  30. package/src/charts/sankey.js +41 -5
  31. package/src/charts/scatter.js +100 -22
  32. package/src/charts/sunburst.js +5 -4
  33. package/src/charts/treemap.js +10 -7
  34. package/src/charts/wordcloud.js +163 -0
  35. package/src/colorscales.js +182 -0
  36. package/src/dashboard.js +438 -0
  37. package/src/dashed.js +1 -1
  38. package/src/data.js +2 -2
  39. package/src/format.js +46 -14
  40. package/src/frame.js +95 -26
  41. package/src/geo/projection.js +74 -9
  42. package/src/geo/registry.js +87 -8
  43. package/src/index.js +76 -11
  44. package/src/infer.js +10 -1
  45. package/src/interact.js +157 -36
  46. package/src/lint.js +289 -9
  47. package/src/runtime.js +209 -97
  48. package/src/scale.js +80 -10
  49. package/src/schema.js +187 -31
  50. package/src/stats.js +71 -14
  51. package/src/theme.js +101 -14
package/index.d.ts CHANGED
@@ -5,15 +5,17 @@ export type ChartType =
5
5
  | 'regplot' | 'residplot' | 'jointplot' | 'hist' | 'kde' | 'ecdf'
6
6
  | 'box' | 'violin' | 'strip' | 'swarm' | 'point' | 'radar'
7
7
  | 'treemap' | 'sunburst' | 'donut' | 'pie' | 'heatmap' | 'sankey' | 'kpi'
8
+ | 'network' | 'chord' | 'pyramid' | 'flowchart' | 'gantt' | 'wordcloud' | 'choropleth' | 'symbolmap' | 'pointmap'
8
9
  // Aliases (seaborn and others)
9
10
  | 'barplot' | 'countplot' | 'lineplot' | 'scatterplot' | 'histplot' | 'kdeplot' | 'ecdfplot'
10
- | 'lmplot' | 'boxplot' | 'violinplot' | 'stripplot' | 'swarmplot' | 'beeswarm' | 'pointplot' | 'spider';
11
+ | 'lmplot' | 'boxplot' | 'violinplot' | 'stripplot' | 'swarmplot' | 'beeswarm' | 'pointplot' | 'spider'
12
+ | 'timeline' | 'roadmap' | 'schedule' | 'cronograma' | 'map' | 'mapa';
11
13
 
12
14
  export type Row = Record<string, string | number | Date | null | undefined>;
13
15
 
14
16
  export type Format =
15
17
  | 'number' | 'compact' | 'percent' | 'currency' | 'integer'
16
- | { type?: string; prefix?: string; suffix?: string; decimals?: number; currency?: string; scale?: number };
18
+ | { type?: 'number' | 'compact' | 'percent' | 'currency' | 'integer'; prefix?: string; suffix?: string; /** 0–20. */ decimals?: number; currency?: string; scale?: number };
17
19
 
18
20
  export interface KpiItem {
19
21
  label: string;
@@ -49,8 +51,31 @@ export interface Theme {
49
51
  }
50
52
 
51
53
  type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? (T[K] extends any[] ? T[K] | string : DeepPartial<T[K]>) : T[K] };
52
- /** JSON theme: deep-merged over `extends` (default "papel"). */
53
- export type ThemeInput = 'papel' | 'noche' | 'light' | 'dark' | (DeepPartial<Theme> & { extends?: 'papel' | 'noche' })
54
+ /** Simple theme tokens, mapped onto the full theme. */
55
+ export interface ThemeTokens {
56
+ /** Alias of surface. */
57
+ background?: string;
58
+ /** Alias of ink. */
59
+ text?: string;
60
+ /** First series color. */
61
+ accent?: string;
62
+ /** Grid color, or { color, dash, width }. */
63
+ grid?: string | { color?: string; dash?: string; width?: number };
64
+ axis?: string;
65
+ tooltip?: { bg?: string; ink?: string };
66
+ /** Corner radius of bars and tiles. */
67
+ radius?: number;
68
+ barRadius?: number;
69
+ font?: string | { family?: string; mono?: string; import?: string };
70
+ }
71
+ /** JSON theme: deep-merged over `extends` (default "papel"). Strings: built-in or registerTheme() names. */
72
+ export type ThemeInput = 'papel' | 'noche' | 'light' | 'dark' | (string & {}) | (Omit<DeepPartial<Theme>, 'grid' | 'font'> & ThemeTokens & { extends?: 'papel' | 'noche' | (string & {}) })
73
+
74
+ /** matplotlib colormap name (case-insensitive, `_r` reverses), a list of hex colors, or { name | colors, reverse, mid }. */
75
+ export type ColorScale = string | string[] | { name?: string; colors?: string[]; reverse?: boolean; mid?: number };
76
+ export type LegendPosition = 'top' | 'bottom' | 'left' | 'right';
77
+ export type TileProvider = 'osm' | 'esri-gray' | 'esri-dark' | 'carto-light' | 'carto-dark' | 'mapbox-light' | 'mapbox-dark';
78
+ export type NetworkLayout = 'force' | 'circular' | 'radial' | 'concentric' | 'clusters' | 'tree' | 'grid' | 'circle';
54
79
 
55
80
  export interface Spec {
56
81
  type?: ChartType;
@@ -75,7 +100,7 @@ export interface Spec {
75
100
  yFormat?: Format;
76
101
  stack?: boolean | 'percent';
77
102
  horizontal?: boolean;
78
- sort?: 'desc' | 'asc' | 'none' | 'alpha';
103
+ sort?: 'desc' | 'asc' | 'none' | 'alpha' | 'start';
79
104
  top?: number;
80
105
  highlight?: string | string[];
81
106
  /** Dashed shapes (hatched fill + dashed outline): names, a data field, { field, equals } or true. */
@@ -86,10 +111,10 @@ export interface Spec {
86
111
  dashedLabel?: string | false;
87
112
  labels?: boolean | 'auto' | number | string[];
88
113
  labelsFor?: Record<string, string>;
89
- refLines?: RefLine[];
114
+ refLines?: RefLine[] | RefLine;
90
115
  annotations?: Annotation[];
91
116
  index?: boolean;
92
- curve?: 'linear' | 'smooth';
117
+ curve?: 'linear' | 'smooth' | 'step';
93
118
  zero?: boolean;
94
119
  points?: boolean;
95
120
  xScale?: 'linear' | 'log';
@@ -100,6 +125,7 @@ export interface Spec {
100
125
  aggregate?: 'sum' | 'mean' | 'max' | 'min' | 'count' | 'last' | 'first';
101
126
  colors?: Record<string, string>;
102
127
  theme?: ThemeInput;
128
+ /** Palette name ("okabe-ito", "tab10", "Set2"…, or any colormap, sampled) or a list of colors. */
103
129
  palette?: string | string[];
104
130
  ramp?: string | string[];
105
131
  hue?: string;
@@ -110,8 +136,6 @@ export interface Spec {
110
136
  panelHeight?: number;
111
137
  time?: string;
112
138
  timeValue?: string | number;
113
- slider?: boolean;
114
- play?: boolean;
115
139
  compare?: 'previous' | 'first' | string | number | false;
116
140
  compareData?: Row[];
117
141
  colorBy?: 'group' | 'change' | 'label';
@@ -143,12 +167,20 @@ export interface Spec {
143
167
  annotate?: boolean;
144
168
  kind?: 'scatter' | 'reg';
145
169
  marginal?: 'hist' | 'kde';
146
- inner?: 'box' | 'quartiles' | 'points' | 'none';
170
+ inner?: 'box' | 'quartiles' | 'points' | 'stick' | 'none';
147
171
  split?: boolean;
148
172
  axis?: string;
149
173
  axes?: string[];
150
174
  normalize?: boolean;
151
- legend?: boolean;
175
+ /** Legend on/off or position (default top; left/right fall back to bottom under 520px). */
176
+ legend?: boolean | LegendPosition | { position?: LegendPosition };
177
+ legendTitle?: string;
178
+ /** Drop legend entries (series, first level, group); colors stay fixed and the entries stay in the legend, faded. */
179
+ exclude?: string | string[];
180
+ /** Continuous color scale. Aliases: cmap, colormap. */
181
+ colorScale?: ColorScale;
182
+ cmap?: ColorScale;
183
+ colormap?: ColorScale;
152
184
  locale?: 'es' | 'en';
153
185
  width?: number;
154
186
  height?: number;
@@ -161,10 +193,112 @@ export interface Spec {
161
193
  centerLabel?: string;
162
194
  midpoint?: number;
163
195
  diverging?: boolean;
164
- /** choropleth/symbolmap: "chile" (default), "usa", "world", a registered name or a GeoJSON FeatureCollection. */
165
- geo?: 'chile' | 'usa' | 'world' | string | object;
196
+ /** choropleth/symbolmap: "chile" (default), "usa", "world", a registered name, your own GeoJSON (FeatureCollection, Feature, Feature[] or geometry) or TopoJSON. In the browser, render() also accepts a URL. */
197
+ geo?: 'chile' | 'usa' | 'world' | string | object | object[];
198
+ /** TopoJSON: object to draw (default: the first). */
199
+ geoObject?: string;
166
200
  key?: string;
167
- projection?: 'mercator' | 'equalEarth' | 'albersUsa';
201
+ /** Feature property matched against `key` (auto-detected when omitted). */
202
+ featureKey?: string;
203
+ /** Feature property with the display name. */
204
+ nameKey?: string;
205
+ projection?: 'mercator' | 'equalEarth' | 'albersUsa' | 'equirectangular' | 'conic' | 'identity' | 'planar' | { type: string; parallels?: [number, number]; rotate?: number; center?: [number, number] };
206
+ /** Zoom to regions, a [lonMin, latMin, lonMax, latMax] box, or "markers". */
207
+ fitTo?: string | string[] | [number, number, number, number];
208
+ markers?: Array<{ lon: number; lat: number; label?: string; value?: number; color?: string }> | { data: Row[]; lon?: string; lat?: string; label?: string; size?: string; color?: string; legend?: string; maxRadius?: number; radius?: number; labels?: boolean };
209
+ layers?: Array<{ geo: object | string; label?: string; stroke?: string; width?: number; fill?: string; dash?: boolean | string; opacity?: number; below?: boolean; object?: string }>;
210
+ borderColor?: string;
211
+ borderWidth?: number;
212
+ pointRadius?: number;
213
+ /** network: node rows (id, group, size). flowchart: steps (id, label, type, lane). */
214
+ nodes?: Row[];
215
+ /** flowchart: connections (from, to, label). Same as `data`. */
216
+ edges?: Row[];
217
+ layout?: NetworkLayout;
218
+ /** network: edge curvature, 0 straight (default 0.3). */
219
+ curveness?: number;
220
+ nodeId?: string;
221
+ nodeLabel?: string;
222
+ /** Largest node / circle radius in px. */
223
+ maxRadius?: number;
224
+ /** network: force-layout iterations. */
225
+ iterations?: number;
226
+ /** chord: square flow matrix with `names`. */
227
+ matrix?: number[][];
228
+ names?: string[];
229
+ directed?: boolean;
230
+ ribbonColor?: 'source' | 'smaller' | 'larger';
231
+ labelOrientation?: 'horizontal' | 'radial';
232
+ direction?: 'TB' | 'LR';
233
+ /** pyramid */
234
+ age?: string;
235
+ sex?: string;
236
+ percent?: boolean;
237
+ /** pointmap: tile basemap with lat/lon dots. */
238
+ lat?: string;
239
+ lon?: string;
240
+ tiles?: TileProvider | { url: string; attribution?: string; subdomains?: string; maxZoom?: number; retina?: boolean } | false;
241
+ /** Public token / API key for the tile provider (a secret Mapbox "sk." token is rejected). */
242
+ tilesKey?: string;
243
+ tileStyle?: 'muted' | 'original';
244
+ tileOpacity?: number;
245
+ zoom?: number;
246
+ /** [lon, lat] */
247
+ center?: [number, number];
248
+ /** choropleth */
249
+ missing?: 'hatch' | 'plain';
250
+ thresholds?: number[];
251
+ classes?: number;
252
+ scale?: 'quantile' | 'equal' | 'continuous' | 'categorical' | number[] | string;
253
+ values?: boolean;
254
+ tall?: boolean;
255
+ /** Seaborn-style options */
256
+ orient?: 'v' | 'h';
257
+ valueDomain?: [number, number];
258
+ bandwidth?: number;
259
+ bwAdjust?: number;
260
+ cut?: number;
261
+ jitter?: number | boolean;
262
+ dodge?: boolean;
263
+ hueOrder?: string[];
264
+ showMeans?: boolean;
265
+ cumulative?: boolean;
266
+ rug?: boolean;
267
+ complementary?: boolean;
268
+ radius?: number;
269
+ endLabels?: boolean;
270
+ /** treemap/sunburst */
271
+ showValues?: boolean;
272
+ hole?: number;
273
+ thickness?: number;
274
+ cellHeight?: number;
275
+ facetOrder?: string[];
276
+ /** gantt */
277
+ task?: string;
278
+ start?: string;
279
+ end?: string;
280
+ duration?: string;
281
+ progress?: string;
282
+ dependsOn?: string;
283
+ milestone?: string;
284
+ today?: string | number | boolean;
285
+ groupHeaders?: boolean | 'compact';
286
+ rowHeight?: number;
287
+ /** gantt: phases drawn collapsed into a summary row; true for all. Click toggles in the browser. */
288
+ collapsed?: boolean | string | string[];
289
+ collapsible?: boolean;
290
+ /** gantt: highlight the zero-slack chain along dependsOn. */
291
+ critical?: boolean;
292
+ criticalColor?: string;
293
+ /** gantt: shade weekends on day plans of up to 120 days (default true). */
294
+ weekends?: boolean;
295
+ todayColor?: string;
296
+ /** Browser time controls, off by default: true (buttons or slider), "buttons" or "slider". */
297
+ slider?: boolean | 'buttons' | 'slider';
298
+ play?: boolean;
299
+ /** wordcloud: words field, or raw text to count. */
300
+ text?: string | string[];
301
+ textField?: string;
168
302
  columns?: number;
169
303
  [key: string]: unknown;
170
304
  }
@@ -194,16 +328,53 @@ export declare function resolve(spec: Spec): Spec;
194
328
  export declare const schema: Record<string, unknown>;
195
329
  /** Library-wide defaults (font, textScale, locale, theme, palette…). The spec always wins. `configure(null)` resets. */
196
330
  export declare function configure(options?: Partial<Spec> | null): Partial<Spec>;
197
- export declare function registerGeo(name: string, featureCollection: object, aliases?: string[]): object;
331
+ export interface DashboardSpec {
332
+ title?: string; subtitle?: string; source?: string; updated?: string; note?: string;
333
+ locale?: 'es' | 'en'; theme?: Spec['theme']; font?: Spec['font']; palette?: string | string[];
334
+ data?: Row[];
335
+ kpis?: Array<{ label: string; value?: number; delta?: number; period?: string; trend?: number[]; good?: 'up' | 'down'; format?: Format;
336
+ field?: string; agg?: 'sum' | 'mean' | 'median' | 'min' | 'max' | 'count' | 'distinct' | 'ratio'; over?: string; scale?: number;
337
+ compare?: { field: string; current: string | number; previous: string | number }; trendBy?: string }>;
338
+ charts: Array<Spec & { primary?: boolean; size?: 'full' | 'large' | 'medium' | 'small'; span?: number }>;
339
+ filters?: Array<string | { field: string; label?: string; control?: 'buttons' | 'select'; value?: string }>;
340
+ layout?: 'auto' | 'executive' | 'grid';
341
+ colors?: Record<string, string>;
342
+ }
343
+ /** Self-contained HTML dashboard with best-practice layout, shared colors, KPIs and filters. */
344
+ export declare function dashboard(spec: DashboardSpec): string;
345
+ /** Never throws: { ok, html, plan, errors, warnings, suggestions }. */
346
+ export declare function safeDashboard(spec: DashboardSpec): { ok: boolean; html: string | null; plan: object | null; errors: Issue[]; warnings: Issue[]; suggestions: Issue[] };
347
+ export declare function lintDashboard(spec: DashboardSpec): { ok: boolean; errors: Issue[]; warnings: Issue[]; suggestions: Issue[] };
348
+ export declare function planDashboard(spec: DashboardSpec): object;
349
+ /** Browser: draws a dashboard into an element. */
350
+ export declare function renderDashboard(target: string | Element, spec: DashboardSpec): { update(): void; destroy(): void };
351
+ export declare const dashboardSchema: Record<string, unknown>;
352
+ /** Raw text → [{ text, value }] word counts without stopwords. */
353
+ export declare function countWords(text: string | string[], opts?: { minLength?: number; stopwords?: string[] }): { text: string; value: number }[];
354
+ export declare function registerGeo(name: string, geo: object, aliases?: string[]): object;
355
+ /** Any GeoJSON / TopoJSON shape → FeatureCollection. */
356
+ export declare function toFeatureCollection(geo: object | object[], objectName?: string): object;
357
+ /** Browser: downloads (once per URL) a GeoJSON or TopoJSON. */
358
+ export declare function loadGeo(url: string): Promise<object>;
198
359
  export declare const palettes: Record<string, string[]>;
199
360
  export declare const ramps: Record<string, string[]>;
200
361
  export declare function themeFor(spec: Spec): Theme;
201
362
  export declare function timeValues(spec: Spec): (string | number)[];
202
363
  export declare function validatePalette(colors: string[], opts?: { mode?: 'light' | 'dark'; surface?: string; pairs?: 'adjacent' | 'all' }): { ok: boolean; checks: { name: string; status: 'pass' | 'warn' | 'fail'; detail: string }[] };
203
364
  export declare function bundleSource(): string | null;
204
- export declare function toolDefinition(opts?: { name?: string; flavor?: 'anthropic' | 'openai' }): Record<string, unknown>;
205
- export declare const themes: Record<'papel' | 'noche' | 'light' | 'dark', Theme>;
365
+ export declare function toolDefinition(opts?: { name?: string; flavor?: 'anthropic' | 'openai'; kind?: 'chart' | 'dashboard' }): Record<string, unknown>;
366
+ export declare const themes: Record<'papel' | 'noche' | 'light' | 'dark', Theme> & Record<string, Theme>;
206
367
  export declare function getTheme(t?: Spec['theme']): Theme;
368
+ /** Registers a named theme (merged over `extends`, default papel); use it as theme: "name" or configure({ theme: "name" }). */
369
+ export declare function registerTheme(name: string, theme: ThemeInput): Theme;
370
+ /** Resolves a color scale to its stops, or null when the name is unknown. */
371
+ export declare function resolveColorScale(v: ColorScale): { name: string; stops: string[]; kind: 'sequential' | 'diverging' | 'cyclic' | 'qualitative' } | null;
372
+ /** Available colormap and palette names by kind. */
373
+ export declare function colorScaleNames(): { sequential: string[]; diverging: string[]; cyclic: string[]; qualitative: string[] };
374
+ /** Color at t ∈ [0, 1] along `stops` (OKLab). */
375
+ export declare function interpolateColor(stops: string[], t: number): string;
376
+ export declare const COLORMAPS: Record<string, string>;
377
+ export declare const QUALITATIVE: Record<string, string>;
207
378
  export declare const TYPES: ChartType[];
208
379
  export declare function squarify<T extends { value: number }>(items: T[], x: number, y: number, w: number, h: number): (T & { x: number; y: number; w: number; h: number })[];
209
380
  export declare function makeFormat(fmt?: Format, locale?: string, values?: number[]): ((v: number) => string) & { type: string };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "plotagents",
3
- "version": "0.5.0",
4
- "description": "Beautiful charts from one JSON spec, built for AI agents: bars, lines, multi-level treemaps, sunbursts, sankeys and seaborn-style plots (regplot, hist, kde, box, violin…). Accessible SVG, time sliders, JSON themes, zero dependencies.",
3
+ "version": "0.7.0",
4
+ "description": "The chart protocol for AI agents: one JSON spec in, a finished accessible SVG out. Bars, lines, Gantt charts, treemaps, sankeys, maps from any GeoJSON/TopoJSON, networks and seaborn-style plots. Zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "module": "./src/index.js",
@@ -57,7 +57,13 @@
57
57
  "ai",
58
58
  "agents",
59
59
  "llm",
60
- "tool-use"
60
+ "tool-use",
61
+ "gantt",
62
+ "timeline",
63
+ "geojson",
64
+ "topojson",
65
+ "choropleth",
66
+ "map"
61
67
  ],
62
68
  "author": "Carlos Navarrete <cnavarreteliz@gmail.com>",
63
69
  "license": "MIT",