@aparte/plugin-echarts 0.0.0-stage → 0.29.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/LICENSE +21 -0
- package/README.md +200 -2
- package/dist/core.d.ts +64 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +974 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/option.d.ts +63 -0
- package/dist/option.d.ts.map +1 -0
- package/dist/strings.d.ts +103 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/theme.d.ts +56 -0
- package/dist/theme.d.ts.map +1 -0
- package/package.json +64 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aparté
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,201 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @aparte/plugin-echarts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Charts for [aparté](https://github.com/apartejs/aparte) via [Apache ECharts](https://echarts.apache.org).
|
|
4
|
+
Every ```` ```echarts ```` fence in a reply — an ECharts `option` as JSON — is drawn as a chart, as
|
|
5
|
+
it streams, whichever Markdown provider is installed, or none. A tool whose result is an option
|
|
6
|
+
can be drawn the same way, in place of its row.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @aparte/plugin-echarts @aparte/core echarts
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { setupECharts } from '@aparte/plugin-echarts';
|
|
14
|
+
|
|
15
|
+
setupECharts();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`@aparte/core` and `echarts` (6.0 or later) are **peer dependencies**. ECharts is loaded on the
|
|
19
|
+
first chart, not before, and a chart is drawn only once it comes near the screen, so reopening a
|
|
20
|
+
long conversation stays fast.
|
|
21
|
+
|
|
22
|
+
## The fence
|
|
23
|
+
|
|
24
|
+
The body is ECharts' own `option`, as JSON — nothing new to learn, and what a model already
|
|
25
|
+
knows from ECharts' documentation. `echart` works as the language too.
|
|
26
|
+
|
|
27
|
+
````md
|
|
28
|
+
```echarts
|
|
29
|
+
{
|
|
30
|
+
"title": { "text": "Sales by month" },
|
|
31
|
+
"tooltip": { "trigger": "axis" },
|
|
32
|
+
"xAxis": { "type": "category", "data": ["Jan", "Feb", "Mar"] },
|
|
33
|
+
"yAxis": { "type": "value" },
|
|
34
|
+
"series": [{ "name": "Sales", "type": "bar", "data": [120, 200, 150] }]
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
````
|
|
38
|
+
|
|
39
|
+
While the reply streams, the chart is drawn from the part of the JSON that has arrived — its
|
|
40
|
+
last whole value, brackets closed — at most every 300 ms, so the bars appear as their numbers
|
|
41
|
+
do; each frame updates the same chart. Once the fence closes, one that is not valid JSON — or is,
|
|
42
|
+
but has no `series` and no `dataset` to draw — shows a note and its source.
|
|
43
|
+
|
|
44
|
+
## A tool's result
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
setupECharts({
|
|
48
|
+
tool: 'plot_sales', // a name, a RegExp, or an array of them
|
|
49
|
+
map: (result, input) => ({ // optional: your tool's data → an ECharts option
|
|
50
|
+
title: { text: String(input.metric) },
|
|
51
|
+
xAxis: { type: 'category', data: result.months },
|
|
52
|
+
yAxis: {},
|
|
53
|
+
series: [{ type: 'line', data: result.values }],
|
|
54
|
+
}),
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Without `map`, the result itself is the option: its `structuredContent` when it has one, else its
|
|
59
|
+
text read as JSON. While the tool runs, a quiet box stands where the chart will be; waiting for
|
|
60
|
+
approval, refused, stopped or failed, a one-line note says so.
|
|
61
|
+
|
|
62
|
+
## The chat's colours
|
|
63
|
+
|
|
64
|
+
The chart takes its look from the chat's tokens — text (`--aparte-text`), axis labels
|
|
65
|
+
(`--aparte-text-muted`), axis and grid lines (`--aparte-border`), the tooltip's ground
|
|
66
|
+
(`--aparte-surface-1`), the font (`--aparte-font-family`), a transparent background — as an
|
|
67
|
+
ECharts **theme**, so anything the option sets itself wins over it. A light/dark switch recolours
|
|
68
|
+
the chart in place (`setTheme`), without drawing it again.
|
|
69
|
+
|
|
70
|
+
The series colours start with `--aparte-primary`, then eight hues validated for colour-vision
|
|
71
|
+
deficiency (each adjacent pair at least 8 ΔE in OKLab ×100 under protanopia, deuteranopia and
|
|
72
|
+
tritanopia, 15 for full colour vision), stepped once for a light ground and once for a dark one
|
|
73
|
+
so a series keeps its hue across the switch. The primary takes the place of the hue nearest to
|
|
74
|
+
it — a brass primary replaces the yellow — so no two look alike side by side. They are
|
|
75
|
+
`ECHARTS_SERIES_HUES`; `echartsPalette(primary, dark)` gives the order.
|
|
76
|
+
|
|
77
|
+
`echartsTheme(tokens)` is the theme itself, pure — the same tokens make the same theme, in Node
|
|
78
|
+
as in a browser — for a chart of your own that should look like the chat's:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import * as echarts from 'echarts';
|
|
82
|
+
import { echartsTheme, type EChartsTokens } from '@aparte/plugin-echarts';
|
|
83
|
+
|
|
84
|
+
const tokens: EChartsTokens = {
|
|
85
|
+
text: '#241f2b', muted: '#6d6479', border: '#e5ddce',
|
|
86
|
+
surface: '#fffdf8', background: '#f6f2ea', primary: '#b07d33',
|
|
87
|
+
};
|
|
88
|
+
echarts.init(document.getElementById('my-chart')!, echartsTheme(tokens), { renderer: 'svg' });
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Size, motion, screen readers
|
|
92
|
+
|
|
93
|
+
The chart fills the bubble's width, up to 40rem, at `height` (18rem by default), and follows its
|
|
94
|
+
box as it resizes. It is drawn with ECharts' SVG renderer: crisp at any zoom, and its text is
|
|
95
|
+
real text. Under `prefers-reduced-motion` nothing moves: animation is off at the root, on every
|
|
96
|
+
series, on `graphic` and in a `baseOption`, a timeline does not play itself, an `effectScatter`
|
|
97
|
+
ripples only on hover and a `lines` trail is not drawn.
|
|
98
|
+
|
|
99
|
+
It is a `figure` named by the option's `title.text`, else "Chart". Inside it, ECharts' own
|
|
100
|
+
description is on (`aria.enabled`, in the chat's language when that language is registered with
|
|
101
|
+
ECharts, English otherwise) in the plugin's own settings — an option cannot turn its label off or
|
|
102
|
+
replace it with a `description` of its own; only its `aria.decal` is kept. A visually hidden table
|
|
103
|
+
holds the numbers when the option says plainly what they are: a `dataset` (its first row a header
|
|
104
|
+
the way ECharts reads `sourceHeader`), a category axis with its series, or a pie's named values. A
|
|
105
|
+
series with no data has no column, and the table stops at 1000 rows, 50 columns and 10 000 cells,
|
|
106
|
+
whatever the option asks — a last line, hidden too, says when it lists only part of the data.
|
|
107
|
+
|
|
108
|
+
Its words follow the chat's language (ten of them built in) and can be set with `extendLocale()`:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
aparteGlobalConfig.extendLocale({
|
|
112
|
+
echartsChart: 'Chart', // the name of an untitled chart
|
|
113
|
+
echartsError: 'This chart could not be drawn.',
|
|
114
|
+
echartsLoading: 'Loading chart',
|
|
115
|
+
echartsWaiting: 'Waiting for your approval',
|
|
116
|
+
echartsNotRun: 'No chart: the tool did not run.',
|
|
117
|
+
echartsFailed: 'No chart: the tool failed.',
|
|
118
|
+
echartsTableCut: 'Only part of the data is listed.', // the hidden table's last line, when it is cut
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Options
|
|
123
|
+
|
|
124
|
+
`setupECharts(options?, config?)` takes `EChartsOptions`:
|
|
125
|
+
|
|
126
|
+
| option | default | |
|
|
127
|
+
| --- | --- | --- |
|
|
128
|
+
| `progressive` | `true` | `false` draws a chart once its fence has closed |
|
|
129
|
+
| `height` | `'18rem'` | pixels, or a CSS length |
|
|
130
|
+
| `renderer` | `'svg'` | `'canvas'` for a chart of tens of thousands of points |
|
|
131
|
+
| `tool` | — | the tool(s) whose result is an option |
|
|
132
|
+
| `map` | — | `(result, input) => option` |
|
|
133
|
+
|
|
134
|
+
Both setup functions return a disposer that unregisters the renderers, stops watching the theme
|
|
135
|
+
and disposes every chart they drew.
|
|
136
|
+
|
|
137
|
+
## What a model's option cannot do
|
|
138
|
+
|
|
139
|
+
The option comes from a model or a tool, so `sanitizeEChartsOption(option)` takes out whatever
|
|
140
|
+
ECharts would turn into HTML, a request or a navigation before it is drawn — and only that:
|
|
141
|
+
|
|
142
|
+
- **Tooltips are drawn, never written as HTML.** Every `tooltip` (the root's, a series', a data
|
|
143
|
+
item's) is forced to `renderMode: 'richText'`, and the HTML-only `extraCssText`, `className`,
|
|
144
|
+
`appendTo` and `appendToBody` go. A string `formatter` stays: in rich-text mode ECharts draws it
|
|
145
|
+
as text, so `<b>` shows as `<b>`.
|
|
146
|
+
- **No download, no HTML panel.** `toolbox.feature.saveAsImage` (a download, or a new tab) and
|
|
147
|
+
`toolbox.feature.dataView` (an HTML table with a textarea) go.
|
|
148
|
+
- **No pictures.** A `graphic` element of `type: 'image'`, any value with an `image` (a picture
|
|
149
|
+
fill, a rich-text background) or an `svgElement` (an SVG pattern ECharts builds node by node —
|
|
150
|
+
a `<script>` would run), any `image://…` string (a symbol, a legend or handle icon), and any
|
|
151
|
+
string holding a CSS `url(…)` (a fill pointing at another document) go — read with its CSS
|
|
152
|
+
escapes decoded, since a fill takes `u\rl(` as `url(`; any string pairing a backslash with a
|
|
153
|
+
bracket goes too.
|
|
154
|
+
- **Only colours where a colour goes.** ECharts copies a `color`, `fill`, `stroke` or any `…Color`
|
|
155
|
+
into an SVG attribute as it is, so there only colour syntax stays: a name, `#hex`, or `rgb()` /
|
|
156
|
+
`rgba()` / `hsl()` / `hsla()` of numbers. A gradient is an object ECharts builds itself; its
|
|
157
|
+
stops are checked the same way.
|
|
158
|
+
- **No regular expression.** A dataset `filter` transform's `reg` is compiled and run on every
|
|
159
|
+
value, and a pattern can backtrack for minutes (`^(a+)+$`), so every `reg` goes.
|
|
160
|
+
- **No links.** A string `link` or `sublink` (a title, a treemap or sunburst node — ECharts opens
|
|
161
|
+
it on a click, and `javascript:` would run in the page) goes.
|
|
162
|
+
- **No CSS through a font.** ECharts writes `fontFamily`, `fontSize`, `fontStyle`, `fontWeight`
|
|
163
|
+
and `font` into a `style` attribute; a value with anything beyond a font name in it goes.
|
|
164
|
+
- `__proto__`, `constructor` and `prototype` keys go.
|
|
165
|
+
|
|
166
|
+
Functions cannot arrive in JSON, so a `formatter` or `renderItem` callback never does. The option
|
|
167
|
+
reaches the page's markup once, as the escaped value of an attribute on the chart's element (a fence
|
|
168
|
+
hands core markup, not a node); ECharts draws it into that element, and the table's cells are
|
|
169
|
+
written as text.
|
|
170
|
+
|
|
171
|
+
## Two entry points
|
|
172
|
+
|
|
173
|
+
`@aparte/plugin-echarts` imports the full ECharts build on the first chart. To hand over your
|
|
174
|
+
own — a smaller build with only the charts you use — import from the entry that imports nothing
|
|
175
|
+
from `echarts`:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
import * as echarts from 'echarts/core';
|
|
179
|
+
import { BarChart, LineChart, PieChart } from 'echarts/charts';
|
|
180
|
+
import {
|
|
181
|
+
AriaComponent, DatasetComponent, GridComponent, LegendComponent, TitleComponent, TooltipComponent,
|
|
182
|
+
} from 'echarts/components';
|
|
183
|
+
import { SVGRenderer } from 'echarts/renderers';
|
|
184
|
+
import { setupEChartsWith } from '@aparte/plugin-echarts/core';
|
|
185
|
+
|
|
186
|
+
// `AriaComponent` and `SVGRenderer` are what the plugin itself uses.
|
|
187
|
+
echarts.use([
|
|
188
|
+
BarChart, LineChart, PieChart,
|
|
189
|
+
AriaComponent, DatasetComponent, GridComponent, LegendComponent, TitleComponent, TooltipComponent,
|
|
190
|
+
SVGRenderer,
|
|
191
|
+
]);
|
|
192
|
+
setupEChartsWith(echarts);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`setupEChartsWith` takes the module, or a function returning a promise of it, and any object
|
|
196
|
+
shaped like `EChartsLike` (`init`, returning an `EChartsInstanceLike`: `setOption`, `setTheme`,
|
|
197
|
+
`resize`, `dispose`). A chart whose series type your build lacks shows the note. The `/core`
|
|
198
|
+
entry exports the same helpers and types: `sanitizeEChartsOption`, `echartsTheme`,
|
|
199
|
+
`echartsPalette`, `ECHARTS_SERIES_HUES`, `EChartsOption`, `EChartsTokens`, `EChartsOptions`.
|
|
200
|
+
|
|
201
|
+
ESM only.
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type AparteConfig, type AparteToolMatcher } from '@aparte/core';
|
|
2
|
+
export { sanitizeEChartsOption, type EChartsOption } from './option.js';
|
|
3
|
+
export { echartsTheme, echartsPalette, ECHARTS_SERIES_HUES, type EChartsTokens } from './theme.js';
|
|
4
|
+
/** The part of a chart ECharts hands back from `init` that this plugin calls. */
|
|
5
|
+
export interface EChartsInstanceLike {
|
|
6
|
+
setOption(option: Record<string, unknown>, opts?: {
|
|
7
|
+
notMerge?: boolean;
|
|
8
|
+
}): void;
|
|
9
|
+
setTheme(theme: Record<string, unknown>): void;
|
|
10
|
+
resize(): void;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The part of ECharts this plugin calls. Structural, so this module imports nothing from
|
|
15
|
+
* `echarts` — a bundle that hands over its own build (`echarts/core` with the charts it
|
|
16
|
+
* uses) carries one copy.
|
|
17
|
+
*/
|
|
18
|
+
export interface EChartsLike {
|
|
19
|
+
init(dom: HTMLElement, theme?: Record<string, unknown> | string | null, opts?: {
|
|
20
|
+
renderer?: 'svg' | 'canvas';
|
|
21
|
+
locale?: string;
|
|
22
|
+
}): EChartsInstanceLike;
|
|
23
|
+
}
|
|
24
|
+
export interface EChartsOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Redraw a chart while its fence arrives, from the part of its JSON that has arrived
|
|
27
|
+
* (default `true`). `false` draws it once the fence has closed.
|
|
28
|
+
*/
|
|
29
|
+
progressive?: boolean;
|
|
30
|
+
/** The chart's height: pixels, or a CSS length (`'18rem'`, the default). It fills the width, up to 40rem. */
|
|
31
|
+
height?: number | string;
|
|
32
|
+
/**
|
|
33
|
+
* ECharts' renderer (default `'svg'`): crisp at any zoom, sized by the page like any
|
|
34
|
+
* element, and its text is real text. `'canvas'` for a chart with tens of thousands of
|
|
35
|
+
* points, where an SVG node per mark costs more than a bitmap.
|
|
36
|
+
*/
|
|
37
|
+
renderer?: 'svg' | 'canvas';
|
|
38
|
+
/**
|
|
39
|
+
* The tool (or tools) whose result IS an ECharts option: it is drawn as the chart in
|
|
40
|
+
* place of the tool's row. Its `structuredContent` when it has one, else its text read
|
|
41
|
+
* as JSON — or what `map` makes of it.
|
|
42
|
+
*/
|
|
43
|
+
tool?: AparteToolMatcher | AparteToolMatcher[];
|
|
44
|
+
/**
|
|
45
|
+
* Turn a tool's own result (and the arguments it was called with) into an ECharts option.
|
|
46
|
+
* The result is `any`, as in `@aparte/plugin-cards`: its shape is your tool's, and `map` is
|
|
47
|
+
* where you read it.
|
|
48
|
+
*/
|
|
49
|
+
map?: (result: any, input: Record<string, unknown>) => unknown;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Draw charts with an ECharts you provide — the module, or a function that loads it on the
|
|
53
|
+
* first chart. The entry point for a bundle that controls what it ships:
|
|
54
|
+
* `@aparte/plugin-echarts` itself is this, with `() => import('echarts')`.
|
|
55
|
+
*
|
|
56
|
+
* @returns a disposer that unregisters the renderers, stops watching the theme and
|
|
57
|
+
* disposes every chart on the page.
|
|
58
|
+
* @example
|
|
59
|
+
* import * as echarts from 'echarts';
|
|
60
|
+
* import { setupEChartsWith } from '@aparte/plugin-echarts/core';
|
|
61
|
+
* setupEChartsWith(echarts);
|
|
62
|
+
*/
|
|
63
|
+
export declare function setupEChartsWith(echarts: EChartsLike | (() => Promise<EChartsLike>), options?: EChartsOptions, config?: AparteConfig): () => void;
|
|
64
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,YAAY,EAGjB,KAAK,iBAAiB,EAEzB,MAAM,cAAc,CAAC;AAKtB,OAAO,EAAE,qBAAqB,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEnG,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAChC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAChF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/C,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,mBAAmB,CAAC;CACzJ;AAED,MAAM,WAAW,cAAc;IAC3B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,6GAA6G;IAC7G,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC5B;;;;OAIG;IACH,IAAI,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,CAAC;IAC/C;;;;OAIG;IAEH,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;CAClE;AAmDD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC5B,OAAO,EAAE,WAAW,GAAG,CAAC,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC,EACnD,OAAO,GAAE,cAAmB,EAC5B,MAAM,GAAE,YAAiC,GAC1C,MAAM,IAAI,CAgBZ"}
|