@amcharts/amcharts5-mcp 1.4.0 → 1.5.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.
- package/content/SKILL.md +83 -3
- package/content/flow.md +4 -0
- package/content/hierarchy.md +21 -0
- package/content/map.md +72 -8
- package/content/pie.md +16 -0
- package/content/radar.md +1 -1
- package/content/timeline.md +3 -3
- package/content/venn.md +4 -0
- package/content/wordcloud.md +2 -0
- package/content/xy.md +17 -1
- package/extended/concepts/events.md +14 -0
- package/extended/concepts/serializing/chart-serializer.md +17 -2
- package/extended/concepts/serializing.md +14 -0
- package/extended/reference/ibasecolumnseriesdataitem.md +1 -0
- package/extended/reference/ibasecolumnseriessettings.md +2 -0
- package/extended/reference/ichartserializersettings.md +1 -0
- package/extended/reference/imapchartsettings.md +2 -0
- package/extended/reference/imaplineseriesdataitem.md +1 -0
- package/extended/reference/imaplineseriessettings.md +2 -0
- package/extended/reference/iparsesettings.md +9 -3
- package/extended/reference/irootsettings.md +4 -0
- package/extended/reference/iscrollbarsettings.md +1 -0
- package/extended/reference/iserialchartcontainersettings.md +16 -0
- package/extended/reference/mapchart.md +4 -0
- package/extended/reference/serialchart.md +1 -1
- package/extended/reference/serialchartcontainer.md +34 -0
- package/package.json +1 -1
- package/src/tools.js +73 -21
package/content/SKILL.md
CHANGED
|
@@ -201,6 +201,27 @@ series.set("tooltip", tooltip);
|
|
|
201
201
|
|
|
202
202
|
`getStrokeFromSprite` (default `false`) copies the sprite's stroke color when `true`.
|
|
203
203
|
|
|
204
|
+
### Shared vs per-sprite tooltip
|
|
205
|
+
|
|
206
|
+
Setting **only** `tooltipText` on a sprite (no `tooltip:` instance anywhere on it) is enough to get a hover tooltip: the sprite lazily resolves the **Root's shared default `Tooltip`** (`sprite.getTooltip()` returns it). You only need your own `am5.Tooltip.new(root, {...})` when that element's tooltip must **look different** from the shared one, or on XY charts, where series/axis tooltips are **cursor-driven** rather than hover-driven and need their own instance.
|
|
207
|
+
|
|
208
|
+
So for non-XY charts do not create a Tooltip per series — set `tooltipText` on the right template and style the look once on the shared tooltip:
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
// Where tooltipText goes on non-XY charts
|
|
212
|
+
pieSeries.slices.template.set("tooltipText", "{category}: {value}"); // pie / funnel / venn slices
|
|
213
|
+
hierarchySeries.nodes.template.set("tooltipText", "{category}: {sum}"); // hierarchy nodes
|
|
214
|
+
flowSeries.nodes.nodes.template.set("tooltipText", "{name}"); // flow nodes
|
|
215
|
+
flowSeries.links.template.set("tooltipText", "{sourceId} → {targetId}: {value}"); // flow links
|
|
216
|
+
wordCloud.labels.template.set("tooltipText", "{category}: {value}"); // word cloud words
|
|
217
|
+
polygonSeries.mapPolygons.template.set("tooltipText", "{name}"); // map polygons (map points: the bullet sprite)
|
|
218
|
+
|
|
219
|
+
// Style the shared tooltip once — it lives on root.container (applies everywhere no dedicated Tooltip was set)
|
|
220
|
+
var shared = root.container.get("tooltip");
|
|
221
|
+
shared.set("getFillFromSprite", false);
|
|
222
|
+
shared.get("background").setAll({ fill: am5.color(0x000000), fillOpacity: 0.8 });
|
|
223
|
+
```
|
|
224
|
+
|
|
204
225
|
## Chart title
|
|
205
226
|
|
|
206
227
|
Do NOT add titles as HTML elements — they are outside the canvas and won't appear in exports. Add an `am5.Label` to the container BEFORE the chart, and set `verticalLayout`:
|
|
@@ -300,12 +321,12 @@ series.set("heatRules", [{
|
|
|
300
321
|
| `maxValue` | number | Override auto-calculated max (skips `calculateAggregates`) |
|
|
301
322
|
| `customFunction` | function | `(sprite, min, max, value)` — full control over the rule |
|
|
302
323
|
|
|
303
|
-
**Using heat rules on bullets** — bullets need an explicit `am5.Template
|
|
324
|
+
**Using heat rules on bullets** — bullets need an explicit `am5.Template`, and the series must track the field the rule reads:
|
|
304
325
|
|
|
305
326
|
```js
|
|
306
327
|
var circleTemplate = am5.Template.new({});
|
|
307
328
|
|
|
308
|
-
series.bullets.push(function() {
|
|
329
|
+
series.bullets.push(function(root, series, dataItem) {
|
|
309
330
|
return am5.Bullet.new(root, {
|
|
310
331
|
sprite: am5.Circle.new(root, {
|
|
311
332
|
fill: series.get("fill"),
|
|
@@ -323,6 +344,13 @@ series.set("heatRules", [{
|
|
|
323
344
|
}]);
|
|
324
345
|
```
|
|
325
346
|
|
|
347
|
+
Rules that make bullet heat rules work (each failure is **silent** — every bullet just comes out the same size):
|
|
348
|
+
- `target` must be the **`Template`** the bullet sprites are built from (3rd argument of `am5.Circle.new(...)`), not the sprite and not `series.bullets`.
|
|
349
|
+
- `dataField` is the data item **property key** (`"valueY"`, `"valueX"`, `"value"`, …), **not** the raw column name. To size XY bullets by a column that is not already an axis value, register it as the base value field: `series.set("valueField", "population")` and use `dataField: "value"`.
|
|
350
|
+
- `calculateAggregates: true` is required (unless `minValue`/`maxValue` are given) — without it `valueLow`/`valueHigh` are never computed and every bullet gets the midpoint size.
|
|
351
|
+
- Setting `valueField` (or any `*Field`) **after** the series has processed data does not re-read the data — re-set it: `series.data.setAll(series.data.values.slice())`.
|
|
352
|
+
- `ChartSerializer` calls the bullet factory with a sample data item to introspect the sprite. That throwaway sprite joins the template but has no `dataItem`, and (as of 5.20.5) the heat-rule loop then throws on `target.dataItem` — on a `LineSeries` the stroke silently disappears. If you serialize charts that have bullet heat rules, have the factory call `sprite._setDataItem(dataItem)` itself, or serialize before adding the rule.
|
|
353
|
+
|
|
326
354
|
**HeatLegend:**
|
|
327
355
|
|
|
328
356
|
```js
|
|
@@ -404,6 +432,23 @@ const cursor = chart.set("cursor", am5xy.XYCursor.new(root, {}));
|
|
|
404
432
|
|
|
405
433
|
**Reading animated settings is unreliable mid-animation.** Right after `series.appear()` / `chart.appear()`, animated settings like `opacity` are still transitioning — `get("opacity")` can return `0` (the start value). Read such settings after the animation completes, or don't persist values read during appear (a common way to accidentally bake `opacity:0` into generated code).
|
|
406
434
|
|
|
435
|
+
## Settings that already equal the default — omit them
|
|
436
|
+
|
|
437
|
+
Generated code routinely spells out settings that equal the amCharts default (verified against the default themes of 5.20.5). They add noise and mislead readers into thinking they matter. Do not emit these unless changing them:
|
|
438
|
+
|
|
439
|
+
| Class | Setting(s) that are already the default |
|
|
440
|
+
|-------|------------------------------------------|
|
|
441
|
+
| All hierarchy series | `childDataField: "children"`, `downDepth: 1`, `initialDepth: 5` |
|
|
442
|
+
| `Sunburst`, `Partition`, `Treemap`, `VoronoiTreemap` | `singleBranchOnly: true` (`Partition`/`Treemap` also `upDepth: 0`; `VoronoiTreemap` also `shapeType: "polygon"`) |
|
|
443
|
+
| `Tree`, `ForceDirected` | `singleBranchOnly: false`, `upDepth: Infinity`, `topDepth: 0` (`Tree` also `orientation: "vertical"`) |
|
|
444
|
+
| `PieSeries`, `FunnelSeries` | `alignLabels: true` (`FunnelSeries` also `orientation: "vertical"`, `startLocation: 0`, `endLocation: 1`, `bottomRatio: 0`) |
|
|
445
|
+
| `XYSeries` (all XY series) | `maskBullets: true` — note the default is **true**, so `maskBullets: false` is a real change |
|
|
446
|
+
| `LineSeries` (incl. smoothed/step) | `connect: true`; `StepLineSeries` `noRisers: false` |
|
|
447
|
+
| `XYChart` | `maxTooltipDistance` — leave **unset** (unset shows tooltips for all items in the category; `0` is *not* the same) |
|
|
448
|
+
| `HeatLegend` | `stepCount: 1` |
|
|
449
|
+
|
|
450
|
+
Runtime-mutated settings (`visible`, `x`/`y`, `opacity`, `scale`, cursor line `visible`, physics/random layouts, computed `AxisRendererCurve.points`) cannot be checked by "remove and re-`get()`" — amCharts rewrites them after render. User-set values live in `entity._userProperties` (what `ChartSerializer` reads), separate from the full `_settings` bag that includes theme and internal values.
|
|
451
|
+
|
|
407
452
|
## Dynamic data
|
|
408
453
|
|
|
409
454
|
```js
|
|
@@ -555,6 +600,8 @@ colors.set("colors", [
|
|
|
555
600
|
]);
|
|
556
601
|
```
|
|
557
602
|
|
|
603
|
+
**A `ColorSet` must always hold at least one color.** `colorSet.set("colors", [])` crashes the next `next()`/`getIndex()` call (`Cannot read properties of undefined (reading 'toHSL')` inside `generateColors`) as soon as a series iterates the palette — as of 5.20.5 the empty list is not guarded. To "reset" a palette, swap in a fresh `am5.ColorSet.new(root, {})` instead of emptying the list. The theme's `baseColor` also resolves lazily, so a **synchronous** re-color right after swapping a `ColorSet` can read a transient value (sprites momentarily black); re-set the series data and let amCharts' own data pass recolor the sprites.
|
|
604
|
+
|
|
558
605
|
## Data processor
|
|
559
606
|
|
|
560
607
|
```js
|
|
@@ -569,6 +616,17 @@ series.data.processor = am5.DataProcessor.new(root, {
|
|
|
569
616
|
// Configure processor BEFORE setting data
|
|
570
617
|
```
|
|
571
618
|
|
|
619
|
+
## Serializing to JSON (ChartSerializer / JsonParser)
|
|
620
|
+
|
|
621
|
+
`am5plugins_json.ChartSerializer` turns a live chart into a JSON config and `am5plugins_json.JsonParser` rebuilds it (`await parser.parse(config, { parent: root.container })`). Rules that keep round-trips working:
|
|
622
|
+
|
|
623
|
+
- **Serialize the chart's top-level container child, never a bare series.** `serializer.serializeAll(root.container.children.getIndex(0))`. Serializing an unwrapped series whose settings point back at itself (`selectedDataItem` holds a `DataItem` whose `component` is the series) produces a cycle: `_pruneEmptyObjects` overflows the stack (`removeEmptyObjects: true`, the default) or `JSON.stringify` throws "Converting circular structure". With the container as root the series becomes a `#series-0` reference and the cycle disappears.
|
|
624
|
+
- **Adapters do not round-trip.** `includeAdapters: true` writes each adapter as `{ key, callback: "function (…) {…}" }`; the parser does not turn that string back into a function (and the closure's `chart`/`series`/`root` would not exist anyway). Before 5.20.4 the string was registered as a callback and crashed the chart (`i[s] is not a function` in `Entity.fold`); since 5.20.4 such adapters are skipped. Either way the effect is lost — replace palette-coloring adapters with declarative equivalents: `colorByDataItem: true` on column series (5.20.4), a per-item `fill` data field + `templateField`, or `heatRules`.
|
|
625
|
+
- **Only user-set settings are serialized (5.20.3)** — theme and library defaults are left out, so a serialized config is much smaller than before and a value you never set will not appear in it. Add `includeRoot: true` (5.20.2) to also write a top-level `root` section (Root settings/properties, `interfaceColors`, formatters).
|
|
626
|
+
- **Re-parsing into an existing chart:** `parser.parse(config, { updateTargets: "soft" })` (5.20.3) applies settings onto an existing object of the same `type` instead of replacing it. The default `"strict"` replaces.
|
|
627
|
+
- **Not captured by `ChartSerializer` (as of 5.20.5)** — re-apply these in code after `parse()`: a `ZoomableContainer`'s `contents.children` and its `ZoomTools` (the serialized container comes back empty — track "chart is zoomable" out-of-band, or use `am5.SerialChartContainer` which builds the zoomable wrapper itself); Venn `hoverGraphics` and Venn slice-template `states` (a custom Venn hover cannot be expressed in JSON); custom elements without `themeTags: ["serialize"]`.
|
|
628
|
+
- **Bullets** are captured by calling the factory with a sample data item — write factories as `function(root, series, dataItem)` (pitfall #36) and see the heat-rule caveat under "Heat rules".
|
|
629
|
+
|
|
572
630
|
## Accessibility
|
|
573
631
|
|
|
574
632
|
```js
|
|
@@ -721,6 +779,10 @@ onUnmounted(() => { root.dispose(); });
|
|
|
721
779
|
31. **Globe rotation uses negative coordinates** — To center the globe (`geoOrthographic`) on a geographic point, set `rotationX` to **-longitude** and `rotationY` to **-latitude**. E.g., to center on Paris (48.86°N, 2.35°E): `chart.animate({ key: "rotationX", to: -2.35 }); chart.animate({ key: "rotationY", to: -48.86 });`. Using positive values rotates the globe the wrong way.
|
|
722
780
|
32. **`positionOnLine` with multi-segment lines limits per-segment control** — `MapPointSeries` data items can animate along a line via `positionOnLine` (0→1). If the line has 3+ points (multi-segment), position 0.5 is the midpoint of the *entire* path, making per-segment effects (scaling at each segment midpoint, pausing between segments, etc.) difficult. For advanced per-segment animations, use **single-segment lines** (2 points each) and animate the bullet across them sequentially. E.g., instead of one line [A,B,C,D], create [A,B], [B,C], [C,D].
|
|
723
781
|
33. **Do NOT exclude Antarctica (`exclude: ["AQ"]`) by default** — Many amCharts demos exclude Antarctica because they use Mercator projection where it appears disproportionately large. This is a demo-specific choice, not a best practice. Unless the user explicitly asks to exclude Antarctica, or references a demo that does so, keep Antarctica in the map. With non-Mercator projections (`geoNaturalEarth1`, `geoEqualEarth`, `geoOrthographic`, `geoEquirectangular`), Antarctica renders at a reasonable size.
|
|
782
|
+
34. **Custom GeoJSON for `MapPolygonSeries` needs CLOCKWISE exterior rings** — amCharts projects on a sphere (d3-geo), where ring winding decides which side is "inside". The RFC 7946 default is counter-clockwise, which d3-geo reads as *the whole globe minus the polygon*, so every polygon floods the entire map and the real shape shows only as a sliver. Programmatically generated GeoJSON (grids, cells, buffers) is usually CCW. amCharts does **not** auto-rewind — reverse each ring yourself: `feature.geometry.coordinates = feature.geometry.coordinates.map(ring => ring.slice().reverse())` (for `MultiPolygon`, one level deeper). See `references/map.md`.
|
|
783
|
+
35. **`axis.dispose()` does NOT remove the axis from `chart.xAxes`/`chart.yAxes`** — unlike `series.dispose()`, which self-removes from `chart.series`. A disposed axis stays in the list, keeps showing up in anything built from `chart.yAxes`, and makes `ChartSerializer` throw `Template is disposed`. Remove axes with `chart.yAxes.removeValue(axis)` (or `removeIndex(i)`) — the list auto-disposes the removed axis, no separate `dispose()` needed.
|
|
784
|
+
36. **Declare bullet callbacks as `function(root, series, dataItem)`** — amCharts passes all three; closing over an outer `root` works at runtime but breaks JSON round-trips (`ChartSerializer` captures the function source, and the outer `root` dangles on re-bind). Every `series.bullets.push(...)` in these references uses the parameter form — copy it.
|
|
785
|
+
37. **A `ColorSet` must contain at least one color** — `colors.set("colors", [])` crashes the next `next()`/`getIndex()`. Swap in a fresh `am5.ColorSet.new(root, {})` instead. See "ColorSet" above.
|
|
724
786
|
|
|
725
787
|
## Easing functions
|
|
726
788
|
|
|
@@ -758,7 +820,7 @@ Skip this step entirely if you cannot execute code (e.g., chat-only context with
|
|
|
758
820
|
|
|
759
821
|
## Recent API changes (newer than the bundled class reference)
|
|
760
822
|
|
|
761
|
-
The bundled per-class API reference was snapshotted on **2026-03-15**, so it predates the changes below. Latest release covered here: **5.20.
|
|
823
|
+
The bundled per-class API reference was snapshotted on **2026-03-15**, so it predates the changes below. Latest release covered here: **5.20.5** (2026-09-03). Prefer these names/settings; for anything newer, verify against the live docs (see next section).
|
|
762
824
|
|
|
763
825
|
**Renamed settings (old name still works but is deprecated — use the new one):**
|
|
764
826
|
|
|
@@ -787,6 +849,8 @@ am5hierarchy.VoronoiTreemap.new(root, { shapeType: "rectangle" }); // was: type
|
|
|
787
849
|
- `XYChart`: `strokeWidths` (array of pixel widths) and `strokeDasharrays` (array of dash arrays) — cycled across line series as they are added, exactly like `colors`. Lets series be told apart without relying on color, e.g. with the `Patterns` theme.
|
|
788
850
|
- `XYCursor`: `clickTolerance` (default `0`) — how many pixels outside the plot area a press may start and still begin a zoom/selection. The selection itself still starts at the plot edge.
|
|
789
851
|
- `XYSeries`: a value field (`valueYField`, `openValueYField`, …) can now be changed after creation — re-set the series data afterwards for it to take effect. A series can also be reassigned to a different `xAxis`/`yAxis` after creation.
|
|
852
|
+
- Column series (`ColumnSeries`, `CandlestickSeries`, `OHLCSeries`, …) (5.20.4): `colorByDataItem: true` (default `false`) gives each column its own color from the series' new `colors` (`ColorSet`) setting — the series makes its own `ColorSet` if none is given. The color lands on the data item's `fill` field. Prefer this over the old "adapter on `columns.template` `fill`" recipe: it is declarative and survives JSON serialization.
|
|
853
|
+
- `Scrollbar` (5.20.2): `opposite: true` (default `false`) puts a chart's `scrollbarX` **below** the plot and `scrollbarY` to the **left**, instead of above / right. Only affects scrollbars set via `chart.set("scrollbarX"/"scrollbarY", …)`. This is a **different** setting from the long-standing `opposite` on `AxisRendererX`/`AxisRendererY`.
|
|
790
854
|
|
|
791
855
|
*WordCloud (5.20.1)*
|
|
792
856
|
- `svgPath` arranges words into a shape (experimental), with `maskByShape` to clip them to the outline and `shapeTolerance` to control spill (negative = padding inside). See `references/wordcloud.md`.
|
|
@@ -799,8 +863,23 @@ am5hierarchy.VoronoiTreemap.new(root, { shapeType: "rectangle" }); // was: type
|
|
|
799
863
|
- `Root`: `ariaLabel` is now applied to the `<div>` holding the chart's focusable elements (5.20.0).
|
|
800
864
|
- `Label`: `oversizedBehavior: "truncate"` now ignores `maxHeight` (there is no way to truncate text vertically).
|
|
801
865
|
- A `Container`'s `Rectangle`/`RoundedRectangle` background now defaults `crisp` to `true` unless set explicitly (5.20.0).
|
|
866
|
+
- `Root` (5.20.2): `fontFamily`, `fontSize` (number or string) and `fontWeight` (`"normal" | "bold" | "bolder" | "lighter" | "100"…`"900"`) settings — defaults for **all** text elements unless overridden on an element. Set them in `am5.Root.new("chartdiv", { fontFamily: "Inter" })` or `root.setAll({...})` instead of a custom theme rule on `Label`.
|
|
867
|
+
- `am5.SerialChartContainer` (5.20.2) — a directly usable `SerialChart` whose `seriesContainer` sits inside a `ZoomableContainer` (exposed as `chart.zoomableContainer`), so series such as `ForceDirected` can be zoomed and panned. Exported from the **`am5` root module**, not a chart package. Optional `zoomTools` setting takes a `ZoomTools` instance whose `target` is pointed at the series container automatically. Bullets added to its series are drawn (5.20.4) and flow-chart bullets are placed correctly inside it (5.20.5). See `references/hierarchy.md`.
|
|
868
|
+
|
|
869
|
+
*JSON config / serialization (5.20.2 – 5.20.4)*
|
|
870
|
+
- `ChartSerializer` now emits **only user-set settings** (5.20.3) — theme and library defaults are left out. Anything diffing or post-processing serializer output must expect much smaller configs.
|
|
871
|
+
- A JSON config may carry a top-level **`root`** section (5.20.2) that applies settings and properties to the `Root` object before the chart is parsed; `ChartSerializer` writes it when `includeRoot: true` (default `false`). A `Root`'s `interfaceColors`, `utc`, `fps`, formatters and `tabindex` round-trip through it (5.20.3).
|
|
872
|
+
- `JsonParser.parse(config, { updateTargets: "soft" })` (5.20.3) — an option on the **second argument of `parse()`**, not a parser setting. `"soft"` applies settings onto an existing object of the same `type` instead of replacing it; default `"strict"` replaces. `parse()` is async and returns a Promise.
|
|
873
|
+
- Gantt charts are now serializable/parsable (5.20.3). Cross-references are written as references (`"@series.get('fill')"`) instead of copies; large values (a map's `geoJSON`, a `Root`'s locale) are written as the name of the pack they came from and rebuilt on parse.
|
|
874
|
+
- An adapter whose callback did not survive the JSON round-trip is now **skipped** on parse instead of breaking the chart (5.20.4) — adapters still do not round-trip, see "Serializing to JSON" above.
|
|
875
|
+
|
|
876
|
+
*Bullets on non-XY charts (5.20.2)*
|
|
877
|
+
- `ArcDiagram`: bullets on nodes are now placed on the node circle, `locationX`/`locationY` being fractions of it (before 5.20.2 they were created but never positioned). Flow charts (`Sankey`/`Chord`): node bullets are children of the node, drawn above its shape and below its label, positioned relative to the node. `FunnelSeries`/`PyramidSeries`: bullet `locationX`/`locationY` follow the slice's sloping edges instead of its bounding box.
|
|
878
|
+
- A bullet `Graphics` with no paint of its own takes the color of **its own slice or node** on pie, funnel, flow and hierarchy charts — so it is the same color as what it sits on and **invisible** unless offset, stroked, or given a contrasting `fill` deliberately. Setting `fill` on the bullet sprite turns the inheritance off.
|
|
802
879
|
|
|
803
880
|
*Maps*
|
|
881
|
+
- `MapChart` (5.20.2): `doubleClickZoom` (default **`true`** — a behavior change: a double click/tap zooms in, shift + double click zooms out; set `false` to opt out). On maps panned by rotating (`panX: "rotateX"` / `panY: "rotateY"`) the clicked point rotates to the center, otherwise it stays under the pointer. `boxZoom: "none" | "drag" | "shift" | "ctrl" | "alt"` (default `"none"`) is **not a boolean** — it names the key held while dragging a rectangle to zoom into; `"drag"` needs no key, so pair it with `panX`/`panY: "none"`. The rectangle is the readonly `chart.boxZoomSelection` element (a `Rectangle`). There are no keyboard-navigation settings on `MapChart`.
|
|
882
|
+
- `MapLineSeries` (5.20.3): `pointIds` data field (`{ pointIds: ["JFK", "LAX"] }`) names the `MapPointSeries` points a line connects, looked up in the series given by the new `pointSeries` setting; `pointIdsField` (default `"pointIds"`) renames the field. `pointsToConnect` wins if both are set. See `references/map.md`.
|
|
804
883
|
- `MapChart`: `projectionName` (5.19.0) — string alternative to `projection`, e.g. `"geoOrthographic"`, mainly for JSON config. Register extra ones with `am5map.registerProjection()`. Bundled projections are no longer eagerly imported, so unused ones tree-shake away.
|
|
805
884
|
- `MapSankeySeries` (5.17.0) — Sankey overlaid on a map; auto-resolves `sourceId`/`targetId` once `polygonSeries` geoJSON loads (5.17.1), so no `datavalidated` wrapper needed. See `references/map.md`.
|
|
806
885
|
- `MapPointSeries` defaults changed to `longitudeField: "longitude"`, `latitudeField: "latitude"` (5.16.1).
|
|
@@ -816,6 +895,7 @@ am5hierarchy.VoronoiTreemap.new(root, { shapeType: "rectangle" }); // was: type
|
|
|
816
895
|
|
|
817
896
|
*All entities*
|
|
818
897
|
- `onDebounced(key, cb, delay)` / `offDebounced(key, cb?)` and `onPrivateDebounced` / `offDebouncedPrivate` (5.17.3) — fire once after rapid changes settle.
|
|
898
|
+
- `once(key, cb)` and `onceDebounced(key, cb, delay)` (5.20.4) — like `on()`/`onDebounced()` for a **settings** key, but the callback fires only the first time that setting changes, then removes itself. Both return an `IDisposer`. (Distinct from `events.once("eventName", …)`, which has existed for events all along.)
|
|
819
899
|
- `Label`: `fontFamily: "inherit"` (5.17.3) uses the chart container's computed font.
|
|
820
900
|
|
|
821
901
|
## Verify unfamiliar API before using it
|
package/content/flow.md
CHANGED
|
@@ -254,6 +254,10 @@ series.links.template.events.on("click", function(ev) {
|
|
|
254
254
|
});
|
|
255
255
|
```
|
|
256
256
|
|
|
257
|
+
## Bullets on nodes (5.20.2)
|
|
258
|
+
|
|
259
|
+
Bullets pushed to `series.nodes.bullets` are children of the node since 5.20.2: drawn above the node shape and below its label, positioned relative to the node (`locationX`/`locationY` as fractions of it). On `ArcDiagram` they sit on the node circle — before 5.20.2 they were created but never placed. A bullet `Graphics` with no `fill`/`stroke` of its own takes the **node's** color, i.e. the same color as what it sits on — give it a contrasting `fill`, a `stroke`, or an offset or it is invisible. Node/link tooltips need no `am5.Tooltip` instance: set `tooltipText` on `series.nodes.nodes.template` / `series.links.template` and the root's shared tooltip is used.
|
|
260
|
+
|
|
257
261
|
## Animated bullets along links
|
|
258
262
|
|
|
259
263
|
Animated labels/circles that flow along Sankey or Chord links use `series.bullets.push()` — **NOT** `series.links.template.bullets.push()`. The bullet position (0 = source, 1 = target) is animated in a loop, and an adapter fades opacity based on position.
|
package/content/hierarchy.md
CHANGED
|
@@ -319,6 +319,27 @@ series.events.on("datavalidated", function() {
|
|
|
319
319
|
});
|
|
320
320
|
```
|
|
321
321
|
|
|
322
|
+
## Zoomable / pannable hierarchy — `SerialChartContainer` (5.20.2)
|
|
323
|
+
|
|
324
|
+
`am5.SerialChartContainer` (exported from the **`am5` root module**, not `am5hierarchy`) is a directly usable `SerialChart` whose `seriesContainer` sits inside a `ZoomableContainer`, so a large `ForceDirected`, `Tree` or `Pack` can be wheel-zoomed and dragged. Prefer it over hand-wrapping the chart in `am5.ZoomableContainer` — it also survives `ChartSerializer` round-trips, which a manual `ZoomableContainer` does not (its `contents` are not serialized).
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
const chart = root.container.children.push(am5.SerialChartContainer.new(root, {
|
|
328
|
+
zoomTools: am5.ZoomTools.new(root, {}) // optional +/−/home buttons, target is wired automatically
|
|
329
|
+
}));
|
|
330
|
+
|
|
331
|
+
const series = chart.series.push(am5hierarchy.ForceDirected.new(root, {
|
|
332
|
+
valueField: "value",
|
|
333
|
+
categoryField: "name",
|
|
334
|
+
childDataField: "children"
|
|
335
|
+
}));
|
|
336
|
+
series.data.setAll(data);
|
|
337
|
+
|
|
338
|
+
chart.zoomableContainer.setAll({ maxZoomLevel: 8 }); // the ZoomableContainer holding the series
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Bullets on series inside a `SerialChartContainer` are drawn as of 5.20.4, and flow-chart (`Sankey`/`Chord`/`ArcDiagram`) bullets are placed correctly inside one as of 5.20.5.
|
|
342
|
+
|
|
322
343
|
## Disposal
|
|
323
344
|
|
|
324
345
|
```js
|
package/content/map.md
CHANGED
|
@@ -123,6 +123,28 @@ polygonSeries.data.setAll([
|
|
|
123
123
|
]);
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
+
**Custom GeoJSON: exterior rings must be CLOCKWISE.** amCharts projects on a sphere (d3-geo), where a ring's winding order decides which side is "inside". The GeoJSON spec (RFC 7946) winds exterior rings counter-clockwise, and d3-geo reads a CCW ring as *the whole globe minus the polygon* — so every polygon fills the entire viewport and the real shape shows only as a sliver (looks like a zoom/fit bug, is not). GIS exports are usually already clockwise; programmatically generated GeoJSON (grids, cells, buffers) usually is not. amCharts does **not** auto-rewind, so reverse the rings yourself:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
// Fix winding before handing custom GeoJSON to MapPolygonSeries
|
|
130
|
+
geojson.features.forEach(function(feature) {
|
|
131
|
+
var g = feature.geometry;
|
|
132
|
+
if (g.type === "Polygon") {
|
|
133
|
+
g.coordinates = g.coordinates.map(function(ring) { return ring.slice().reverse(); });
|
|
134
|
+
} else if (g.type === "MultiPolygon") {
|
|
135
|
+
g.coordinates = g.coordinates.map(function(poly) {
|
|
136
|
+
return poly.map(function(ring) { return ring.slice().reverse(); });
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
polygonSeries.set("geoJSON", geojson);
|
|
141
|
+
|
|
142
|
+
// Fit the map to a custom region (no country geodata needed): left/right = longitude, top/bottom = latitude
|
|
143
|
+
chart.zoomToGeoBounds({ left: -8.6, right: 1.8, top: 60.9, bottom: 49.9 }, 500);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`zoomToGeoBounds(bounds, duration?)` is more reliable than `homeGeoPoint` + `homeZoomLevel` + `goHome()` when the only series is custom polygons.
|
|
147
|
+
|
|
126
148
|
### Choropleth (heat map) via heat rules
|
|
127
149
|
|
|
128
150
|
```js
|
|
@@ -145,6 +167,17 @@ const heatLegend = chart.children.push(am5.HeatLegend.new(root, {
|
|
|
145
167
|
}));
|
|
146
168
|
```
|
|
147
169
|
|
|
170
|
+
**Styling heat-legend segments.** With `stepCount: N` the legend draws N discrete `RoundedRectangle` markers inside `heatLegend.markerContainer`, all styled through `heatLegend.markers.template` (it accepts the usual Graphics settings — `stroke`, `strokeWidth`, `strokeOpacity`, `fillOpacity`, `cornerRadius*`, `tooltipText` for a per-block hover tooltip). Segment **thickness** is the marker's cross-axis size: a **vertical** legend uses the marker `width` (default `15`) with `height: 100%`; a **horizontal** legend uses `height` (default `15`) with `width: 100%`. Style via the template — `heatLegend.markers.values` can read empty even while `markerContainer.children` holds the rectangles.
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
heatLegend.markers.template.setAll({
|
|
174
|
+
width: 30, // thicker bar on a vertical legend (use height on a horizontal one)
|
|
175
|
+
stroke: am5.color(0xffffff),
|
|
176
|
+
strokeWidth: 1,
|
|
177
|
+
tooltipText: "{value}"
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
148
181
|
### MapPointSeries (markers, bubbles, labels)
|
|
149
182
|
|
|
150
183
|
```js
|
|
@@ -156,7 +189,7 @@ const pointSeries = chart.series.push(
|
|
|
156
189
|
);
|
|
157
190
|
|
|
158
191
|
// Define bullet appearance
|
|
159
|
-
pointSeries.bullets.push(function() {
|
|
192
|
+
pointSeries.bullets.push(function(root, series, dataItem) {
|
|
160
193
|
return am5.Bullet.new(root, {
|
|
161
194
|
sprite: am5.Circle.new(root, {
|
|
162
195
|
radius: 5,
|
|
@@ -214,8 +247,24 @@ lineSeries.pushDataItem({
|
|
|
214
247
|
coordinates: [[-74.01, 40.71], [-0.45, 51.47]] // [lng, lat] pairs
|
|
215
248
|
}
|
|
216
249
|
});
|
|
250
|
+
|
|
251
|
+
// OR (5.20.3) name the points by id — data-driven, JSON-friendly, no data-item references needed
|
|
252
|
+
const pointSeries2 = chart.series.push(am5map.MapPointSeries.new(root, {})); // ids come from the "id" data field
|
|
253
|
+
pointSeries2.data.setAll([
|
|
254
|
+
{ id: "JFK", latitude: 40.64, longitude: -73.78 },
|
|
255
|
+
{ id: "LAX", latitude: 33.94, longitude: -118.41 }
|
|
256
|
+
]);
|
|
257
|
+
const lineSeries2 = chart.series.push(am5map.MapLineSeries.new(root, {
|
|
258
|
+
pointSeries: pointSeries2 // where pointIds are looked up
|
|
259
|
+
// pointIdsField: "pointIds" // default field name
|
|
260
|
+
}));
|
|
261
|
+
lineSeries2.data.setAll([
|
|
262
|
+
{ pointIds: ["JFK", "LAX"] } // the line waits until every id has a point, then draws
|
|
263
|
+
]);
|
|
217
264
|
```
|
|
218
265
|
|
|
266
|
+
`pointsToConnect` wins if a data item has both. Fixed points (`fixed: true`) cannot be used with either.
|
|
267
|
+
|
|
219
268
|
### MapSankeySeries (geographic flow bands)
|
|
220
269
|
|
|
221
270
|
Draws variable-width curved bands between geographic points on a map. Band thickness represents a numeric value — like a Sankey diagram projected onto a map. Extends `MapPolygonSeries` and generates actual GeoJSON polygon geometries, so bands follow the map projection during pan, zoom, and rotation.
|
|
@@ -365,7 +414,7 @@ var sankeySeries = chart.series.push(
|
|
|
365
414
|
#### Animated bullets along bands
|
|
366
415
|
|
|
367
416
|
```js
|
|
368
|
-
sankeySeries.bullets.push(function() {
|
|
417
|
+
sankeySeries.bullets.push(function(root, series, dataItem) {
|
|
369
418
|
return am5.Bullet.new(root, {
|
|
370
419
|
locationX: 0,
|
|
371
420
|
autoRotate: true,
|
|
@@ -420,7 +469,7 @@ sankeySeries.data.setAll([
|
|
|
420
469
|
```js
|
|
421
470
|
// Create a point that follows a line
|
|
422
471
|
var planeSeries = chart.series.push(am5map.MapPointSeries.new(root, {}));
|
|
423
|
-
planeSeries.bullets.push(function() {
|
|
472
|
+
planeSeries.bullets.push(function(root, series, dataItem) {
|
|
424
473
|
return am5.Bullet.new(root, {
|
|
425
474
|
sprite: am5.Graphics.new(root, {
|
|
426
475
|
svgPath: "m2,106h28l24,30h72l-44,-133h35l80,132h98c21,0 21,34 0,34l-98,0 -80,134h-35l43,-133h-71l-24,30h-28l15,-47",
|
|
@@ -471,6 +520,19 @@ chart.chartContainer.children.push(am5.Button.new(root, {
|
|
|
471
520
|
})).events.on("click", function() {
|
|
472
521
|
chart.goHome();
|
|
473
522
|
});
|
|
523
|
+
|
|
524
|
+
// Fit to a lat/long box (left/right = longitude, top/bottom = latitude)
|
|
525
|
+
chart.zoomToGeoBounds({ left: -10, right: 40, top: 70, bottom: 35 }, 800);
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
**Pointer zoom behaviors (5.20.2):** a double click/tap zooms in and shift + double click zooms out **by default** (`doubleClickZoom: true`; set `false` to turn it off). `boxZoom` lets the user drag a rectangle to zoom into — its value is the key that must be held: `"shift"`, `"ctrl"`, `"alt"`, or `"drag"` for no key (then set `panX`/`panY: "none"`, since a plain drag otherwise pans); default `"none"`. The rectangle being drawn is `chart.boxZoomSelection` (a `Rectangle`, style its `fill`/`stroke`).
|
|
529
|
+
|
|
530
|
+
```js
|
|
531
|
+
am5map.MapChart.new(root, {
|
|
532
|
+
boxZoom: "shift", // hold shift and drag to zoom into the box
|
|
533
|
+
doubleClickZoom: false // opt out of the default double-click zoom
|
|
534
|
+
});
|
|
535
|
+
chart.boxZoomSelection.setAll({ fill: am5.color(0x1e88e5), fillOpacity: 0.15 });
|
|
474
536
|
```
|
|
475
537
|
|
|
476
538
|
## Globe (orthographic projection)
|
|
@@ -552,6 +614,8 @@ am5map.MapChart.new(root, {
|
|
|
552
614
|
rotationZ: 0, // tilt
|
|
553
615
|
wheelY: "zoom", // "zoom", "none"
|
|
554
616
|
wheelSensitivity: 1, // zoom speed multiplier
|
|
617
|
+
doubleClickZoom: true, // (5.20.2) double click zooms in, shift+double click out — default true
|
|
618
|
+
boxZoom: "none", // (5.20.2) "none" | "drag" | "shift" | "ctrl" | "alt" — key held to drag a zoom box
|
|
555
619
|
})
|
|
556
620
|
```
|
|
557
621
|
|
|
@@ -783,7 +847,7 @@ root.dispose();
|
|
|
783
847
|
|
|
784
848
|
// Point series for airports
|
|
785
849
|
var pointSeries = chart.series.push(am5map.MapPointSeries.new(root, {}));
|
|
786
|
-
pointSeries.bullets.push(function() {
|
|
850
|
+
pointSeries.bullets.push(function(root, series, dataItem) {
|
|
787
851
|
return am5.Bullet.new(root, {
|
|
788
852
|
sprite: am5.Circle.new(root, {
|
|
789
853
|
radius: 4,
|
|
@@ -895,7 +959,7 @@ Demonstrates: orthographic projection, single-segment lines for per-segment anim
|
|
|
895
959
|
|
|
896
960
|
// City points with pulse + label
|
|
897
961
|
var pointSeries = chart.series.push(am5map.MapPointSeries.new(root, {}));
|
|
898
|
-
pointSeries.bullets.push(function
|
|
962
|
+
pointSeries.bullets.push(function(root, series, dataItem) {
|
|
899
963
|
var container = am5.Container.new(root, {});
|
|
900
964
|
container.children.push(am5.Circle.new(root, {
|
|
901
965
|
radius: 3.5, fill: cityColor, fillOpacity: 0.9,
|
|
@@ -932,7 +996,7 @@ Demonstrates: orthographic projection, single-segment lines for per-segment anim
|
|
|
932
996
|
|
|
933
997
|
// Plane series — one per segment, all hidden initially
|
|
934
998
|
var planeSeries = chart.series.push(am5map.MapPointSeries.new(root, {}));
|
|
935
|
-
planeSeries.bullets.push(function
|
|
999
|
+
planeSeries.bullets.push(function(root, series, dataItem) {
|
|
936
1000
|
return am5.Bullet.new(root, {
|
|
937
1001
|
sprite: am5.Graphics.new(root, {
|
|
938
1002
|
svgPath: "m2,106h28l24,30h72l-44,-133h35l80,132h98c21,0 21,34 0,34l-98,0 -80,134h-35l43,-133h-71l-24,30h-28l15,-47",
|
|
@@ -1201,7 +1265,7 @@ Demonstrates: `MapSankeySeries` with `sourceId`/`targetId` data (country codes),
|
|
|
1201
1265
|
});
|
|
1202
1266
|
|
|
1203
1267
|
// Animated coffee bean bullets
|
|
1204
|
-
sankeySeries.bullets.push(function() {
|
|
1268
|
+
sankeySeries.bullets.push(function(root, series, dataItem) {
|
|
1205
1269
|
return am5.Bullet.new(root, {
|
|
1206
1270
|
locationX: 0,
|
|
1207
1271
|
autoRotate: true,
|
|
@@ -1492,7 +1556,7 @@ Demonstrates: `MapSankeySeries` with explicit `sourceLongitude`/`sourceLatitude`
|
|
|
1492
1556
|
});
|
|
1493
1557
|
|
|
1494
1558
|
// Animated oil drop bullets
|
|
1495
|
-
sankeySeries.bullets.push(function() {
|
|
1559
|
+
sankeySeries.bullets.push(function(root, series, dataItem) {
|
|
1496
1560
|
return am5.Bullet.new(root, {
|
|
1497
1561
|
locationX: 0,
|
|
1498
1562
|
autoRotate: true,
|
package/content/pie.md
CHANGED
|
@@ -260,6 +260,22 @@ am5percent.PictorialStackedSeries.new(root, {
|
|
|
260
260
|
})
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
+
## Bullets on slices
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
series.bullets.push(function(root, series, dataItem) {
|
|
267
|
+
return am5.Bullet.new(root, {
|
|
268
|
+
locationX: 0.5, locationY: 0.5, // fractions of the slice (funnel/pyramid: along the sloping edges since 5.20.2)
|
|
269
|
+
sprite: am5.Circle.new(root, {
|
|
270
|
+
radius: 6,
|
|
271
|
+
fill: am5.color(0xffffff) // set a paint — an unpainted bullet takes its own slice's color and is invisible
|
|
272
|
+
})
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Percent series have **no series-level `fill`**: a bullet `Graphics` with no `fill`/`stroke` is auto-colored per data item from its own slice (5.20.0+), so it blends in unless stroked, offset, or given a contrasting fill. Slice tooltips need no `am5.Tooltip` instance — `series.slices.template.set("tooltipText", "{category}: {value}")` uses the root's shared tooltip.
|
|
278
|
+
|
|
263
279
|
## Legend (for all percent charts)
|
|
264
280
|
|
|
265
281
|
```js
|
package/content/radar.md
CHANGED
|
@@ -286,7 +286,7 @@ columnSeries.columns.template.events.on("click", function(ev) {
|
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
// Pointer over/out on radar line data points (via bullets)
|
|
289
|
-
series.bullets.push(function() {
|
|
289
|
+
series.bullets.push(function(root, series, dataItem) {
|
|
290
290
|
var circle = am5.Circle.new(root, {
|
|
291
291
|
radius: 5,
|
|
292
292
|
fill: series.get("fill"),
|
package/content/timeline.md
CHANGED
|
@@ -214,7 +214,7 @@ series.set("snapTooltip", true);
|
|
|
214
214
|
Bullets on timeline charts work the same as XY charts:
|
|
215
215
|
|
|
216
216
|
```js
|
|
217
|
-
series.bullets.push(function() {
|
|
217
|
+
series.bullets.push(function(root, series, dataItem) {
|
|
218
218
|
return am5.Bullet.new(root, {
|
|
219
219
|
sprite: am5.Circle.new(root, {
|
|
220
220
|
radius: 5,
|
|
@@ -258,7 +258,7 @@ series.bullets.push(function(root, series, dataItem) {
|
|
|
258
258
|
|
|
259
259
|
```js
|
|
260
260
|
// Click on data points (via bullets)
|
|
261
|
-
series.bullets.push(function() {
|
|
261
|
+
series.bullets.push(function(root, series, dataItem) {
|
|
262
262
|
var circle = am5.Circle.new(root, {
|
|
263
263
|
radius: 5,
|
|
264
264
|
fill: series.get("fill"),
|
|
@@ -346,7 +346,7 @@ chart.get("cursor").events.on("cursormoved", function(ev) {
|
|
|
346
346
|
series.fills.template.setAll({ visible: true, fillOpacity: 0.3 });
|
|
347
347
|
|
|
348
348
|
// Add bullets
|
|
349
|
-
series.bullets.push(function() {
|
|
349
|
+
series.bullets.push(function(root, series, dataItem) {
|
|
350
350
|
return am5.Bullet.new(root, {
|
|
351
351
|
sprite: am5.Circle.new(root, {
|
|
352
352
|
radius: 4,
|
package/content/venn.md
CHANGED
|
@@ -147,6 +147,10 @@ series.labels.template.adapters.add("text", function(text, target) {
|
|
|
147
147
|
});
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
+
## Serialization gotcha: custom hover is not captured
|
|
151
|
+
|
|
152
|
+
`series.hoverGraphics` (the dashed set outline used in the amCharts venn demo) and `slices.template.states.create("hover", …)` are **not** written by `am5plugins_json.ChartSerializer` as of 5.20.5 — a venn rebuilt from JSON loses any custom hover look (`slices.template.tooltipText` does round-trip). Re-apply `hoverGraphics.setAll({...})` in code after `JsonParser.parse()`.
|
|
153
|
+
|
|
150
154
|
## Click events
|
|
151
155
|
|
|
152
156
|
```js
|
package/content/wordcloud.md
CHANGED
|
@@ -79,6 +79,8 @@ series.appear(1000);
|
|
|
79
79
|
| `sequencedInterpolation` | boolean | `true` | Stagger the word reveal instead of showing all at once |
|
|
80
80
|
| `sequencedDelay` | number | `15` | Milliseconds between words when `sequencedInterpolation` is on |
|
|
81
81
|
|
|
82
|
+
**Parser settings and `text`:** `minValue`, `maxCount`, `minWordLength` and `excludeWords` are consumed when `text` is parsed. Since **5.20.0** changing any of them on a live series re-parses `text` automatically. On older versions they only took effect on the next `text` assignment — set them **before** `text`, or re-assign `text` after changing them.
|
|
83
|
+
|
|
82
84
|
### excludeWords example
|
|
83
85
|
|
|
84
86
|
```js
|
package/content/xy.md
CHANGED
|
@@ -95,6 +95,7 @@ chart.set("cursor", am5xy.XYCursor.new(root, {
|
|
|
95
95
|
### Scrollbar
|
|
96
96
|
```js
|
|
97
97
|
chart.set("scrollbarX", am5.Scrollbar.new(root, { orientation: "horizontal" }));
|
|
98
|
+
// opposite: true (5.20.2) puts scrollbarX below the plot / scrollbarY on the left
|
|
98
99
|
```
|
|
99
100
|
|
|
100
101
|
### Legend
|
|
@@ -191,6 +192,10 @@ const xAxis = chart.xAxes.push(am5xy.DateAxis.new(root, {
|
|
|
191
192
|
- `markUnitChange: true` — highlights when time unit changes on axis labels (e.g., new month)
|
|
192
193
|
- `tooltipDateFormat` — format string for cursor tooltip
|
|
193
194
|
|
|
195
|
+
**Live updates — two settings that do not refresh on `set()`:**
|
|
196
|
+
- **Date format maps** (`dateFormats`, `periodChangeDateFormats`, `minorDateFormats`): after `axis.set("dateFormats", {...})` already-rendered labels keep their old text. `axis.markDirtyValues()` is not enough — call **`axis.markDirtySize()`** to make the axis re-run label formatting.
|
|
197
|
+
- **`groupIntervals`** (with `groupData: true`): amCharts only (re)computes grouping when a series' data is set, so changing the allowed intervals on an already-grouped axis does nothing until you re-set the data on every series bound to the axis: `series.data.setAll(series.data.values.slice())`.
|
|
198
|
+
|
|
194
199
|
**Data format:**
|
|
195
200
|
```js
|
|
196
201
|
series.data.setAll([
|
|
@@ -324,7 +329,12 @@ series.columns.template.setAll({
|
|
|
324
329
|
width: am5.percent(80), // column width
|
|
325
330
|
});
|
|
326
331
|
|
|
327
|
-
// Individual column colors
|
|
332
|
+
// Individual column colors (5.20.4) — declarative, serializes to JSON:
|
|
333
|
+
series.set("colorByDataItem", true); // each column takes the next color from series.get("colors")
|
|
334
|
+
// series.set("colors", am5.ColorSet.new(root, { colors: [...] })); // optional own palette (the series makes one if absent)
|
|
335
|
+
// Per-item color from data instead: templateField + a `fill` in each data item (see pie.md "Data-driven slice colors")
|
|
336
|
+
|
|
337
|
+
// Pre-5.20.4 fallback — an adapter (does NOT survive JSON serialization):
|
|
328
338
|
series.columns.template.adapters.add("fill", (fill, target) => {
|
|
329
339
|
return chart.get("colors").getIndex(series.columns.indexOf(target));
|
|
330
340
|
});
|
|
@@ -403,6 +413,7 @@ Enables zooming/panning via scrollbar. Can optionally include a preview chart.
|
|
|
403
413
|
// Simple scrollbar:
|
|
404
414
|
chart.set("scrollbarX", am5.Scrollbar.new(root, {
|
|
405
415
|
orientation: "horizontal"
|
|
416
|
+
// opposite: true // (5.20.2) place below the plot instead of above (scrollbarY: left instead of right)
|
|
406
417
|
}));
|
|
407
418
|
|
|
408
419
|
// Scrollbar with preview chart:
|
|
@@ -610,8 +621,13 @@ var series2 = chart.series.push(am5xy.LineSeries.new(root, {
|
|
|
610
621
|
xAxis: xAxis, yAxis: yAxis2,
|
|
611
622
|
valueYField: "units", valueXField: "date"
|
|
612
623
|
}));
|
|
624
|
+
|
|
625
|
+
// Removing an axis — go through the list, NOT axis.dispose():
|
|
626
|
+
chart.yAxes.removeValue(yAxis2); // the list auto-disposes the removed axis
|
|
613
627
|
```
|
|
614
628
|
|
|
629
|
+
`axis.dispose()` marks the axis disposed but **leaves it in `chart.yAxes`/`chart.xAxes`** (unlike `series.dispose()`, which removes itself from `chart.series`). The stale entry keeps rendering in anything built from the list and makes `ChartSerializer` throw `Template is disposed`. Always use `removeValue(axis)` / `removeIndex(i)`.
|
|
630
|
+
|
|
615
631
|
## Axis titles
|
|
616
632
|
|
|
617
633
|
```js
|
|
@@ -166,6 +166,20 @@ xAxis.onPrivate("selectionMax", function(value, target) {
|
|
|
166
166
|
console.log("End date changed:", end);
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
+
### One-shot handlers (5.20.4)
|
|
170
|
+
|
|
171
|
+
`once(key, callback)` works like `on()` but fires only the first time that setting changes (or is set), then removes itself. `onceDebounced(key, callback, delay)` is the debounced version. Both return an `IDisposer`.
|
|
172
|
+
|
|
173
|
+
series.once("visible", function(visible, target) {
|
|
174
|
+
console.log("First visibility change", visible);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
xAxis.onceDebounced("start", function(start) {
|
|
178
|
+
console.log("Zoom settled at", start);
|
|
179
|
+
}, 300);
|
|
180
|
+
|
|
181
|
+
(Do not confuse with `events.once("eventName", …)`, which has existed for regular events all along.)
|
|
182
|
+
|
|
169
183
|
### Removing
|
|
170
184
|
|
|
171
185
|
Turning off value change events are similar to regular events: we can just use `off()` or `offPrivate()` methods.
|
|
@@ -92,6 +92,16 @@ Include adapters in the output.
|
|
|
92
92
|
|
|
93
93
|
Export functions as references or stringify them.
|
|
94
94
|
|
|
95
|
+
`includeRoot`
|
|
96
|
+
|
|
97
|
+
`boolean`
|
|
98
|
+
|
|
99
|
+
`false`
|
|
100
|
+
|
|
101
|
+
Add a top-level `root` section with the `Root` object's settings and properties (`interfaceColors`, formatters, `utc`, `fps`, `tabindex`) to the output. `JsonParser` applies a `root` section before parsing the chart. *(5.20.2)*
|
|
102
|
+
|
|
103
|
+
**What gets serialized (5.20.3+):** only **user-set** settings — theme and library defaults are left out, so the output is much smaller than in earlier versions and a value you never set will not appear. Chart-set values and data a chart builds for itself (a flow's nodes, a word cloud's words) are not written. Objects pointing at one another are written as references (e.g. `"@series.get('fill')"`) instead of copies, and big values (a map's `geoJSON`, a `Root`'s locale) are written as the name of the pack they came from and rebuilt on parse.
|
|
104
|
+
|
|
95
105
|
The below instructs serializer to include both states and adapters in the output:
|
|
96
106
|
|
|
97
107
|
const serializer = am5plugins\_json.ChartSerializer.new(root, {
|
|
@@ -137,13 +147,17 @@ pieSeries.children.push(am5.Label.new(root, {
|
|
|
137
147
|
- Venn
|
|
138
148
|
- WordCloud
|
|
139
149
|
- MapChart
|
|
150
|
+
- Gantt *(5.20.3)*
|
|
140
151
|
|
|
141
152
|
## Limitations
|
|
142
153
|
|
|
143
154
|
### Limited functionality
|
|
144
155
|
|
|
145
|
-
- Bullets (series or axis): No support for bullets that reference outside objects or context-based bullets (where bullet code depends on context to determine its appearance)
|
|
156
|
+
- Bullets (series or axis): No support for bullets that reference outside objects or context-based bullets (where bullet code depends on context to determine its appearance). Declare bullet factories as `function(root, series, dataItem)` — a factory that closes over an outer `root` dangles on re-bind.
|
|
146
157
|
- Bullets or adapters that rely on global variables. Parser runs in a different scope, thus global variables might not be available.
|
|
158
|
+
- **Adapters do not round-trip.** With `includeAdapters: true` each adapter is written as `{ key, callback: "function (…) {…}" }` (a string) — `JsonParser` does not turn it back into a function. Before 5.20.4 the string was registered as a callback and crashed the chart (`i[s] is not a function` in `Entity.fold`); since 5.20.4 such adapters are skipped on parse. Prefer declarative equivalents (`colorByDataItem` on column series, per-item `fill` + `templateField`, `heatRules`).
|
|
159
|
+
- **Serialize the chart's top-level container child, not a bare series.** A series whose settings point back at itself (e.g. `selectedDataItem`, a `DataItem` whose `component` is the series) forms a cycle; serialized on its own it overflows `removeEmptyObjects` pruning or makes `JSON.stringify` throw "Converting circular structure". As a child of the serialized container it becomes a `#series-0` reference and the cycle disappears.
|
|
160
|
+
- A bullet heat rule's throwaway sprite (created while the serializer introspects the bullet factory) has no `dataItem`; as of 5.20.5 the heat-rule loop then throws and, on a `LineSeries`, the stroke disappears. Have the factory call `sprite._setDataItem(dataItem)` or serialize before adding the rule.
|
|
147
161
|
|
|
148
162
|
### Unsupported features
|
|
149
163
|
|
|
@@ -153,8 +167,9 @@ pieSeries.children.push(am5.Label.new(root, {
|
|
|
153
167
|
- Templates: `setup` function
|
|
154
168
|
- Custom draw functions
|
|
155
169
|
- Events
|
|
170
|
+
- `ZoomableContainer`: its `contents.children` and any `ZoomTools` are not serialized — a serialized `ZoomableContainer` parses back empty. Use `SerialChartContainer` (5.20.2), which builds its own zoomable wrapper, or re-wrap in code after parsing.
|
|
171
|
+
- Venn: `hoverGraphics` and slice-template `states` are not serialized — re-apply a custom hover in code after parsing.
|
|
156
172
|
|
|
157
173
|
### Unsupported chart types
|
|
158
174
|
|
|
159
|
-
- Gantt
|
|
160
175
|
- StockChart
|
|
@@ -659,6 +659,8 @@ Since adapters are functions, they can't technically be part of a serialized JSO
|
|
|
659
659
|
}\]
|
|
660
660
|
}
|
|
661
661
|
|
|
662
|
+
**Adapters and JSON round-trips:** a function reference works only when the config is a JavaScript object in the same scope. Once the config has been through `JSON.stringify` (or `ChartSerializer` with `functionsAs: "string"`), the callback is a string — `JsonParser` does not evaluate it, and since 5.20.4 such an adapter is skipped (before that it was registered as-is and crashed the chart with `i[s] is not a function`). Effects that must survive a round-trip need a declarative equivalent: `colorByDataItem` on column series (5.20.4), a per-item `fill` field with `templateField`, or `heatRules`.
|
|
663
|
+
|
|
662
664
|
## Parsing
|
|
663
665
|
|
|
664
666
|
To parse serialized configs, we'll need:
|
|
@@ -679,6 +681,18 @@ parser.parse({
|
|
|
679
681
|
// ...
|
|
680
682
|
});
|
|
681
683
|
|
|
684
|
+
### Parse options (`IParseSettings`)
|
|
685
|
+
|
|
686
|
+
The second argument of `parse()` is an options object — these are **not** settings of the parser itself:
|
|
687
|
+
|
|
688
|
+
- `parent` — a `Container` to place the parsed chart into.
|
|
689
|
+
- `updateTargets` *(5.20.3)* — `"strict"` (default): when the source object has a `type`, a new object of that type is created and replaces the existing one. `"soft"`: if source and target are the same type, only settings are applied onto the existing object (as if `type` had not been specified); objects are replaced only when the types differ. Use `"soft"` to update a live chart from a modified config without rebuilding it.
|
|
690
|
+
|
|
691
|
+
var parser = am5plugins\_json.JsonParser.new(root);
|
|
692
|
+
parser.parse(config, { parent: root.container, updateTargets: "soft" });
|
|
693
|
+
|
|
694
|
+
A config may also carry a top-level `root` section *(5.20.2)* whose `settings`/`properties` are applied to the `Root` object before the chart is parsed (`ChartSerializer` writes one when `includeRoot: true`).
|
|
695
|
+
|
|
682
696
|
### Post-parse handler
|
|
683
697
|
|
|
684
698
|
The `parse()` method is asynchronous and returns a `Promise`.
|
|
@@ -337,3 +337,4 @@ Extended by: IColumnSeriesDataItem, IRadarColumnSeriesDataItem, ICurveColumnSeri
|
|
|
337
337
|
- **graphics** (`Graphics`) — An actual Graphics element (Column/Slice/Candlestick/OHLC).
|
|
338
338
|
- **legendDataItem** (`DataItem`) — If data items from this series are used to feed a Legend, this will hold a reference to the equivalent Legend data item. Click here for more info
|
|
339
339
|
- **rangeGraphics** (`Array`) — In case axis ranges are added to the series, it creates a separate element (Graphics) for each axis range. This array holds them all.
|
|
340
|
+
- **fill** (`Color`) — Color of the column, when the series colors each data item separately (colorByDataItem). @since 5.20.4
|
|
@@ -86,3 +86,5 @@ Extended by: IColumnSeriesSettings, IRadarColumnSeriesSettings, ICurveColumnSeri
|
|
|
86
86
|
- **adjustBulletPosition** (`undefined | false | true`) — Default true Whether positions of bullets should be calculated based on portion of column currently visual (true) or the whole length/height of the column (false).
|
|
87
87
|
- **clustered** (`undefined | false | true`) — Default true Indicates if series must divvy up available space with other column series (true; default) or take up the whole available space (false). Click here for more info
|
|
88
88
|
- **useLastColorForLegendMarker** (`undefined | false | true`) — If set to true will use color of the last visible column for legend marker. Otherwise, series fill/stroke will be used. @since 5.1.13
|
|
89
|
+
- **colorByDataItem** (`undefined | false | true`) — Default false If set to true, each column gets its own color from `colors`, instead of the whole series sharing one color. @since 5.20.4
|
|
90
|
+
- **colors** (`ColorSet`) — A ColorSet to take colors from when colorByDataItem is set. The series makes its own if none is given, rather than taking the chart's: the chart's palette is stepped once per series, and one palette per series also means editing one does not change the others. @since 5.20.4
|
|
@@ -175,3 +175,4 @@ Extends: ISerializerSettings
|
|
|
175
175
|
|
|
176
176
|
- **includeProjection** (`undefined | false | true`) — Default false Include projection settings in the output. If enabled, the projection function will be included in the output, according to the functionsAs setting.
|
|
177
177
|
- **removeEmptyObjects** (`undefined | false | true`) — Default true Remove empty objects from the output.
|
|
178
|
+
- **includeRoot** (`undefined | false | true`) — Default false If set to true, adds a top-level `root` section with the Root object's settings and properties (interfaceColors, formatters, utc, fps, tabindex) to the serialized output. JsonParser applies a `root` section before parsing the chart. @since 5.20.2
|
|
@@ -75,3 +75,5 @@ Extends: ISerialChartSettings
|
|
|
75
75
|
- **zoomControl** (`ZoomControl`) — A ZoomControl instance. Click here for more info
|
|
76
76
|
- **zoomLevel** (`undefined | number`) — Current zoom level.
|
|
77
77
|
|
|
78
|
+
- **boxZoom** (`"none" | "drag" | "shift" | "ctrl" | "alt"`) — Default "none" Allows zooming the map to an area drawn with a pointer. The value is the key that must be held while dragging; "drag" means no key is needed — since a plain drag pans the map, use "drag" with panX/panY set to "none". The drawn area is represented by the boxZoomSelection element. NOT a boolean. @since 5.20.2
|
|
79
|
+
- **doubleClickZoom** (`undefined | false | true`) — Default true Setting true will make map zoom in on a double click (or double tap), and zoom out on a shift + double click. On maps panned by rotating (panX: "rotateX" and/or panY: "rotateY") the clicked location is rotated to the center of the map; otherwise it stays under the pointer. @since 5.20.2
|
|
@@ -20,3 +20,4 @@ Extended by: IGraticuleSeriesDataItem
|
|
|
20
20
|
- **lineType** (`"curved" | "straight"`) — Default "curved" A line type. "curved" (default) - connects points using shortest distance, which will result in curved lines based on map projection. "straight" - connects points using visually straight lines, and will not cross the -180/180 longitude. @since 5.2.32
|
|
21
21
|
- **mapLine** (`MapLine`) — Related MapLine object.
|
|
22
22
|
- **pointsToConnect** (`Array`) — An array of data items from MapPointSeries to use as line end-points. Note, fixed points can not be used here.
|
|
23
|
+
- **pointIds** (`Array<string>`) — Ids of the points (data items of the series set in pointSeries, matched by their id) a line connects, e.g. `{ pointIds: ["JFK", "LAX"] }`. The line waits for every id to match a point before it is drawn, so it is never drawn short. Fixed points cannot be used, same as with pointsToConnect. Ignored if pointsToConnect is set on the same data item. @since 5.20.3
|
|
@@ -88,3 +88,5 @@ Extended by: IGraticuleSeriesSettings
|
|
|
88
88
|
|
|
89
89
|
- **clipBack** (`undefined | false | true`) — If set to true will hide line segments that are in the invisible range of the map. For example on the side of the globe facing away from the viewer when used with Orthographic projection. NOTE: not all projections have invisible side.
|
|
90
90
|
- **lineType** (`"curved" | "straight"`) — Default "curved" A line type. "curved" (default) - connects points using shortest distance, which will result in curved lines based on map projection. "straight" - connects points using visually straight lines, and will not cross the -180/180 longitude. @since 5.2.24
|
|
91
|
+
- **pointIdsField** (`undefined | string`) — Default "pointIds" A field in data that holds ids of the points a line connects. @since 5.20.3
|
|
92
|
+
- **pointSeries** (`MapPointSeries`) — A MapPointSeries the pointIds of a data item are looked up in. @since 5.20.3
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
title: "IParseSettings"
|
|
3
3
|
type: "interface"
|
|
4
4
|
source: "https://www.amcharts.com/docs/v5/reference/iparsesettings/"
|
|
5
|
-
scraped: "2026-
|
|
5
|
+
scraped: "2026-09-14"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Options object passed as the **second argument** of `JsonParser.parse(config, options)`. These are not settings of the parser object itself.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
```javascript
|
|
11
|
+
const parser = am5plugins_json.JsonParser.new(root);
|
|
12
|
+
const chart = await parser.parse(config, { parent: root.container, updateTargets: "soft" });
|
|
13
|
+
```
|
|
11
14
|
|
|
15
|
+
## Properties
|
|
12
16
|
|
|
17
|
+
- **parent** (`Container`) — Parent container to place the parsed chart into.
|
|
18
|
+
- **updateTargets** (`"strict" | "soft"`) — Default "strict" If set to "strict" and source object has `type` set, a new object of that type will be created and used in place of existing one. In case of "soft", parser will check if source and target objects are of the same type and overwrite only if types differ. Otherwise only settings will be applied, just like if the `type` was not specified at all. @since 5.20.3
|
|
@@ -43,3 +43,7 @@ If set to some string, it will be used as inner <div> ARIA-LABEL.
|
|
|
43
43
|
- **role** (`undefined | string`) — Allows setting a "role" for the inner <div>. @since 5.3.17 Click here for more info
|
|
44
44
|
- **tooltipContainerBounds** (`undefined | { bottom: number, left: number, right: number, top: number }`) — Allows defining margins around chart area for tooltips to go outside the chart itself. @since 5.2.24
|
|
45
45
|
|
|
46
|
+
- **fontFamily** (`undefined | string`) — Default font family for all text elements, unless overridden on an element. @since 5.20.2
|
|
47
|
+
- **fontSize** (`undefined | number | string`) — Default font size for all text elements, unless overridden on an element. @since 5.20.2
|
|
48
|
+
- **fontWeight** (`"normal" | "bold" | "bolder" | "lighter" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900"`) — Default font weight for all text elements, unless overridden on an element. @since 5.20.2
|
|
49
|
+
- **sanitizeHTML** (`undefined | false | true`) — Default true If set to true, dynamically-set HTML (html/labelHTML, HTML tooltips, modal content) is sanitized. Set to false to opt out. @since 5.19.0
|
|
@@ -52,3 +52,4 @@ Extended by: ISliderSettings, IXYChartScrollbarSettings
|
|
|
52
52
|
- **end** (`undefined | number`) — Relative end of the selected range, with 0 meaning beginning, and 1 the end.
|
|
53
53
|
- **orientation** (`"horizontal" | "vertical"`) — Orientation of the scrollbar.
|
|
54
54
|
- **start** (`undefined | number`) — Relative start of the selected range, with 0 meaning beginning, and 1 the end.
|
|
55
|
+
- **opposite** (`undefined | false | true`) — Default false If set to true, a chart's scrollbarX is placed below the plot area and its scrollbarY to the left, instead of above and to the right. Only has effect on scrollbars set via a chart's scrollbarX/scrollbarY settings; a scrollbar the user added to a container themselves is left where it is. Unrelated to the `opposite` setting of AxisRendererX/AxisRendererY. @since 5.20.2
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "ISerialChartContainerSettings"
|
|
3
|
+
type: "interface"
|
|
4
|
+
source: "https://www.amcharts.com/docs/v5/reference/iserialchartcontainersettings/"
|
|
5
|
+
scraped: "2026-09-14"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Inheritance
|
|
9
|
+
|
|
10
|
+
Extends: ISerialChartSettings
|
|
11
|
+
|
|
12
|
+
> **Note:** This class also inherits all settings, properties, methods, and events from ISerialChartSettings (and its ancestors). Use `get_doc` or `get_core_reference` with the parent class name to see inherited members.
|
|
13
|
+
|
|
14
|
+
## Properties
|
|
15
|
+
|
|
16
|
+
- **zoomTools** (`ZoomTools`) — A ZoomTools instance to add to the chart. When set, its `target` is automatically pointed at the chart's `seriesContainer`. @since 5.20.2
|
|
@@ -66,3 +66,7 @@ Extends: SerialChart
|
|
|
66
66
|
- **wheelY** (`"none" | "zoom" | "rotateX" | "rotateY"`) — Default "zoom" Defines what happens when mouse wheel is turned. Click here for more info
|
|
67
67
|
- **zoomControl** (`ZoomControl`) — A ZoomControl instance. Click here for more info
|
|
68
68
|
- **zoomLevel** (`undefined | number`) — Current zoom level.
|
|
69
|
+
|
|
70
|
+
## Properties
|
|
71
|
+
|
|
72
|
+
- **boxZoomSelection** (`Rectangle`) — A Rectangle element that shows the area being drawn when boxZoom is enabled. Read-only; style its fill/stroke. @since 5.20.2
|
|
@@ -17,7 +17,7 @@ import * as am5 from "@amcharts/amcharts5"
|
|
|
17
17
|
## Inheritance
|
|
18
18
|
|
|
19
19
|
Extends: Chart
|
|
20
|
-
Extended by: PercentChart, XYChart, MapChart
|
|
20
|
+
Extended by: PercentChart, XYChart, MapChart, SerialChartContainer
|
|
21
21
|
|
|
22
22
|
> **Note:** This class also inherits all settings, properties, methods, and events from Chart (and its ancestors). Use `get_doc` or `get_core_reference` with the parent class name to see inherited members.
|
|
23
23
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "SerialChartContainer"
|
|
3
|
+
type: "class"
|
|
4
|
+
source: "https://www.amcharts.com/docs/v5/reference/serialchartcontainer/"
|
|
5
|
+
scraped: "2026-09-14"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A `SerialChart` whose `seriesContainer` is a `ZoomableContainer`, so that the chart contents can be zoomed and panned.
|
|
9
|
+
|
|
10
|
+
Unlike the abstract `SerialChart`, this chart attaches its `seriesContainer` to the display tree on its own, so it can be instantiated and used directly — e.g. to make a `ForceDirected`, `Tree` or `Pack` series zoomable/pannable. Bullets sit alongside the series inside the zoomable container, so they zoom and pan with it.
|
|
11
|
+
|
|
12
|
+
@since 5.20.2
|
|
13
|
+
|
|
14
|
+
## Import
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
// Import SerialChartContainer — exported from the root module, not a chart package
|
|
18
|
+
import * as am5 from "@amcharts/amcharts5"
|
|
19
|
+
const chart = root.container.children.push(am5.SerialChartContainer.new(root, {}));
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Inheritance
|
|
23
|
+
|
|
24
|
+
Extends: SerialChart
|
|
25
|
+
|
|
26
|
+
> **Note:** This class also inherits all settings, properties, methods, and events from SerialChart (and its ancestors). Use `get_doc` or `get_core_reference` with the parent class name to see inherited members.
|
|
27
|
+
|
|
28
|
+
## Settings
|
|
29
|
+
|
|
30
|
+
- **zoomTools** (`ZoomTools`) — A ZoomTools instance to add to the chart. When set, its `target` is automatically pointed at the chart's `seriesContainer`.
|
|
31
|
+
|
|
32
|
+
## Properties
|
|
33
|
+
|
|
34
|
+
- **zoomableContainer** (`ZoomableContainer`) — Default ZoomableContainer.new() A ZoomableContainer which holds chart's `seriesContainer` so it can be zoomed and panned. Theme tag `serialchartcontainer` (default theme: `wheelable: false`, `pinchZoom: false`, `maskContent: true`).
|
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -12,6 +12,26 @@ import { CHART_TYPE_MAP, extractImportBlock, searchDocs } from "./content.js";
|
|
|
12
12
|
export function registerTools(server, content) {
|
|
13
13
|
const { docs, extendedDocs, examples } = content;
|
|
14
14
|
|
|
15
|
+
// Resolve any identifier a search result prints — "skill/SKILL.md",
|
|
16
|
+
// "skill/xy", "SKILL", "xy.md", "extended/charts/xy-chart/cursor",
|
|
17
|
+
// "charts/xy-chart/cursor", "reference/xycursor" — to a doc. Search results
|
|
18
|
+
// label their source exactly this way, so an agent can paste the label
|
|
19
|
+
// straight into get_section / get_doc.
|
|
20
|
+
function resolveDoc(id) {
|
|
21
|
+
let key = String(id || "").trim().replace(/^\/+|\/+$/g, "").replace(/\.md$/i, "");
|
|
22
|
+
if (!key) return null;
|
|
23
|
+
let skillKey = key.startsWith("skill/") ? key.slice("skill/".length) : key;
|
|
24
|
+
if (docs.has(skillKey)) return { doc: docs.get(skillKey), label: `skill/${skillKey}` };
|
|
25
|
+
const extKey = key.startsWith("extended/") ? key.slice("extended/".length) : key;
|
|
26
|
+
if (extendedDocs.has(extKey)) return { doc: extendedDocs.get(extKey), label: `extended/${extKey}` };
|
|
27
|
+
// Case-insensitive skill fallback ("skill" → "SKILL", "Xy" → "xy").
|
|
28
|
+
const lower = skillKey.toLowerCase();
|
|
29
|
+
for (const [name, doc] of docs) {
|
|
30
|
+
if (name.toLowerCase() === lower) return { doc, label: `skill/${name}` };
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
15
35
|
// --- Tool: list_chart_types ---
|
|
16
36
|
server.tool(
|
|
17
37
|
"list_chart_types",
|
|
@@ -86,12 +106,39 @@ export function registerTools(server, content) {
|
|
|
86
106
|
return { content: [{ type: "text", text: `No results found for "${query}".${hint}` }] };
|
|
87
107
|
}
|
|
88
108
|
|
|
109
|
+
// Per-result excerpt budget. When only one or two sections match, the
|
|
110
|
+
// whole point is to read them — grow the budget instead of cutting the
|
|
111
|
+
// answer off.
|
|
112
|
+
const budget = results.length <= 2 ? 6000 : 2000;
|
|
113
|
+
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
114
|
+
|
|
89
115
|
let text = `# Search results for "${query}" (scope: ${scope})\n\n`;
|
|
90
116
|
for (const r of results) {
|
|
117
|
+
const id = `${r.source}/${r.file}`;
|
|
91
118
|
text += `## ${r.docTitle} → ${r.heading}\n`;
|
|
92
|
-
text += `*(source: ${
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
text += `*(source: ${id}.md, relevance: ${r.score})*\n\n`;
|
|
120
|
+
let body = r.body;
|
|
121
|
+
if (body.length > budget) {
|
|
122
|
+
// The excerpt is a fixed-size head, so the line that matched the
|
|
123
|
+
// query may lie past the cut. Quote the matching lines beyond the
|
|
124
|
+
// head so the answer is never lost, and print the exact call that
|
|
125
|
+
// returns the full section.
|
|
126
|
+
const head = body.slice(0, budget);
|
|
127
|
+
const beyond = [];
|
|
128
|
+
let offset = 0;
|
|
129
|
+
for (const line of body.split("\n")) {
|
|
130
|
+
const start = offset;
|
|
131
|
+
offset += line.length + 1;
|
|
132
|
+
if (start < budget) continue;
|
|
133
|
+
const l = line.toLowerCase();
|
|
134
|
+
if (terms.some(t => l.includes(t))) beyond.push(line.trim().slice(0, 400));
|
|
135
|
+
if (beyond.length >= 8) break;
|
|
136
|
+
}
|
|
137
|
+
body = head + `\n\n...(truncated — full section: get_section(file: "${id}", heading: "${r.heading}"))`;
|
|
138
|
+
if (beyond.length) {
|
|
139
|
+
body += `\n\nMatching lines beyond the excerpt:\n` + beyond.map(l => `> ${l}`).join("\n");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
95
142
|
text += body + "\n\n---\n\n";
|
|
96
143
|
}
|
|
97
144
|
return { content: [{ type: "text", text }] };
|
|
@@ -101,28 +148,29 @@ export function registerTools(server, content) {
|
|
|
101
148
|
// --- Tool: get_section ---
|
|
102
149
|
server.tool(
|
|
103
150
|
"get_section",
|
|
104
|
-
"Get
|
|
151
|
+
"Get the FULL text of one section of a doc by heading — use it to expand a truncated search_docs / search_all result. `file` accepts the source label exactly as search results print it ('skill/SKILL.md', 'skill/xy', 'extended/charts/xy-chart/cursor', 'reference/xycursor') as well as a bare skill file name ('SKILL', 'xy', 'map').",
|
|
105
152
|
{
|
|
106
|
-
file: z.string().describe("
|
|
107
|
-
heading: z.string().describe("Section heading to retrieve, e.g. 'Core setup pattern', 'Axis types', 'Common pitfalls'"),
|
|
153
|
+
file: z.string().describe("Doc identifier: a skill file ('SKILL', 'xy', 'pie', 'map') or a source label from search results ('skill/SKILL.md', 'extended/concepts/events', 'reference/xycursor'). '.md' and 'skill/' / 'extended/' prefixes are optional."),
|
|
154
|
+
heading: z.string().describe("Section heading to retrieve (case-insensitive substring), e.g. 'Core setup pattern', 'Axis types', 'Common pitfalls', 'Recent API changes'"),
|
|
108
155
|
},
|
|
109
156
|
async ({ file, heading }) => {
|
|
110
|
-
const
|
|
111
|
-
if (!
|
|
112
|
-
|
|
157
|
+
const resolved = resolveDoc(file);
|
|
158
|
+
if (!resolved) {
|
|
159
|
+
const skillFiles = [...docs.keys()].map(k => `skill/${k}`).join(", ");
|
|
160
|
+
return { content: [{ type: "text", text: `File "${file}" not found.\n\nSkill files: ${skillFiles}\n\nExtended docs use their path, e.g. 'charts/xy-chart/cursor', 'concepts/events', 'reference/xycursor' (with or without an 'extended/' prefix). Use search_docs / search_all to find one.` }] };
|
|
113
161
|
}
|
|
162
|
+
const { doc, label } = resolved;
|
|
114
163
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
);
|
|
164
|
+
const needle = heading.toLowerCase();
|
|
165
|
+
const section = doc.sections.find(s => s.heading.toLowerCase().includes(needle));
|
|
118
166
|
if (!section) {
|
|
119
167
|
const available = doc.sections.map(s => s.heading).filter(Boolean).join("\n- ");
|
|
120
168
|
return {
|
|
121
|
-
content: [{ type: "text", text: `Section "${heading}" not found in ${
|
|
169
|
+
content: [{ type: "text", text: `Section "${heading}" not found in ${label}.md.\n\nAvailable sections:\n- ${available}` }],
|
|
122
170
|
};
|
|
123
171
|
}
|
|
124
172
|
return {
|
|
125
|
-
content: [{ type: "text", text: `# ${section.heading}\n\n${section.body}` }],
|
|
173
|
+
content: [{ type: "text", text: `# ${section.heading}\n*(source: ${label}.md)*\n\n${section.body}` }],
|
|
126
174
|
};
|
|
127
175
|
}
|
|
128
176
|
);
|
|
@@ -244,22 +292,26 @@ export function registerTools(server, content) {
|
|
|
244
292
|
// --- Tool: get_doc ---
|
|
245
293
|
server.tool(
|
|
246
294
|
"get_doc",
|
|
247
|
-
"Get a full documentation page
|
|
295
|
+
"Get a full documentation page — an extended doc by path, or a skill file by 'skill/<name>'. Accepts the source label exactly as search_docs / search_all print it. Use search_all first to find the right path.",
|
|
248
296
|
{
|
|
249
|
-
path: z.string().describe("Doc path, e.g. 'charts/xy-chart/cursor', 'concepts/events', 'getting-started/integrations/react', 'reference/xycursor' (per-class API). For a class's full settings + defaults prefer get_api_reference."),
|
|
297
|
+
path: z.string().describe("Doc path, e.g. 'charts/xy-chart/cursor', 'concepts/events', 'getting-started/integrations/react', 'reference/xycursor' (per-class API), or a skill file such as 'skill/SKILL.md' / 'skill/xy'. For a class's full settings + defaults prefer get_api_reference."),
|
|
250
298
|
},
|
|
251
299
|
async ({ path: docPath }) => {
|
|
252
|
-
const key = docPath.replace(/^\/|\/$/g, "").replace(/\.md$/, "");
|
|
253
|
-
|
|
254
|
-
|
|
300
|
+
const key = String(docPath || "").replace(/^\/|\/$/g, "").replace(/\.md$/, "");
|
|
301
|
+
// Extended docs take precedence for bare paths; 'skill/…' always means the skill layer.
|
|
302
|
+
const resolved = key.startsWith("skill/")
|
|
303
|
+
? resolveDoc(key)
|
|
304
|
+
: (extendedDocs.has(key) ? { doc: extendedDocs.get(key), label: `extended/${key}` } : resolveDoc(key));
|
|
305
|
+
if (!resolved) {
|
|
255
306
|
// List available top-level paths
|
|
256
307
|
const paths = [...extendedDocs.keys()];
|
|
257
308
|
const topLevel = [...new Set(paths.map(p => p.split("/")[0]))].sort();
|
|
258
309
|
return {
|
|
259
|
-
content: [{ type: "text", text: `Doc "${key}" not found.\n\nAvailable top-level sections: ${topLevel.join(", ")}\n\nUse search_all to find the right path.` }],
|
|
310
|
+
content: [{ type: "text", text: `Doc "${key}" not found.\n\nAvailable top-level sections: skill, ${topLevel.join(", ")}\n\nSkill files: ${[...docs.keys()].map(k => `skill/${k}`).join(", ")}\n\nUse search_all to find the right path.` }],
|
|
260
311
|
};
|
|
261
312
|
}
|
|
262
|
-
|
|
313
|
+
const { doc, label } = resolved;
|
|
314
|
+
return { content: [{ type: "text", text: `# ${doc.title}\n*(source: ${label}.md)*\n\n${doc.content}` }] };
|
|
263
315
|
}
|
|
264
316
|
);
|
|
265
317
|
|