@bonnard/cli 0.2.15 → 0.3.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.
@@ -0,0 +1,327 @@
1
+ # Chart.js + Bonnard SDK
2
+
3
+ > Build HTML dashboards with Chart.js and the Bonnard SDK. No build step required.
4
+
5
+ Chart.js is the recommended chart library for HTML dashboards — smallest payload (~65KB gzip), most LLM training data, and excellent documentation.
6
+
7
+ ## Starter template
8
+
9
+ Copy this complete HTML file as a starting point. Replace `bon_pk_YOUR_KEY_HERE` with your publishable API key, and update the view/measure/dimension names to match your schema.
10
+
11
+ Use `explore()` to discover available views and fields — see [sdk.query-reference](sdk.query-reference).
12
+
13
+ ```html
14
+ <!DOCTYPE html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="UTF-8">
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ <title>Dashboard</title>
20
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
21
+ <script src="https://cdn.jsdelivr.net/npm/@bonnard/sdk/dist/bonnard.iife.js"></script>
22
+ <style>
23
+ * { margin: 0; padding: 0; box-sizing: border-box; }
24
+ body {
25
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
26
+ background: #09090b; color: #fafafa; padding: 24px;
27
+ }
28
+ h1 { font-size: 24px; font-weight: 600; margin-bottom: 24px; }
29
+ .error { color: #ef4444; background: #1c0a0a; padding: 12px; border-radius: 8px; margin-bottom: 16px; display: none; }
30
+ .kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 32px; }
31
+ .kpi { background: #18181b; border: 1px solid #27272a; border-radius: 12px; padding: 20px; }
32
+ .kpi-label { font-size: 14px; color: #a1a1aa; margin-bottom: 8px; }
33
+ .kpi-value { font-size: 32px; font-weight: 600; }
34
+ .kpi-value.loading { color: #3f3f46; }
35
+ .charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 24px; }
36
+ .chart-card { background: #18181b; border: 1px solid #27272a; border-radius: 12px; padding: 20px; }
37
+ .chart-title { font-size: 16px; font-weight: 500; margin-bottom: 16px; }
38
+ .chart-container { position: relative; height: 300px; }
39
+ </style>
40
+ </head>
41
+ <body>
42
+ <h1>Dashboard</h1>
43
+ <div id="error" class="error"></div>
44
+
45
+ <div class="kpis">
46
+ <div class="kpi">
47
+ <div class="kpi-label">Revenue</div>
48
+ <div class="kpi-value loading" id="kpi-revenue">--</div>
49
+ </div>
50
+ <div class="kpi">
51
+ <div class="kpi-label">Orders</div>
52
+ <div class="kpi-value loading" id="kpi-orders">--</div>
53
+ </div>
54
+ <div class="kpi">
55
+ <div class="kpi-label">Avg Value</div>
56
+ <div class="kpi-value loading" id="kpi-avg">--</div>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="charts">
61
+ <div class="chart-card">
62
+ <div class="chart-title">Revenue by City</div>
63
+ <div class="chart-container"><canvas id="bar-chart"></canvas></div>
64
+ </div>
65
+ <div class="chart-card">
66
+ <div class="chart-title">Revenue Trend</div>
67
+ <div class="chart-container"><canvas id="line-chart"></canvas></div>
68
+ </div>
69
+ </div>
70
+
71
+ <script>
72
+ const bon = Bonnard.createClient({
73
+ apiKey: 'bon_pk_YOUR_KEY_HERE',
74
+ });
75
+
76
+ // --- Helpers ---
77
+ function showError(msg) {
78
+ const el = document.getElementById('error');
79
+ el.textContent = msg;
80
+ el.style.display = 'block';
81
+ }
82
+
83
+ function formatNumber(v) {
84
+ return new Intl.NumberFormat().format(v);
85
+ }
86
+
87
+ function formatCurrency(v) {
88
+ return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(v);
89
+ }
90
+
91
+ // Chart.js defaults for dark mode
92
+ Chart.defaults.color = '#a1a1aa';
93
+ Chart.defaults.borderColor = '#27272a';
94
+
95
+ // --- Load data ---
96
+ (async () => {
97
+ try {
98
+ // KPIs
99
+ const kpis = await bon.query({
100
+ measures: ['orders.revenue', 'orders.count', 'orders.avg_value'],
101
+ });
102
+ if (kpis.data.length > 0) {
103
+ const row = kpis.data[0];
104
+ document.getElementById('kpi-revenue').textContent = formatCurrency(row['orders.revenue']);
105
+ document.getElementById('kpi-revenue').classList.remove('loading');
106
+ document.getElementById('kpi-orders').textContent = formatNumber(row['orders.count']);
107
+ document.getElementById('kpi-orders').classList.remove('loading');
108
+ document.getElementById('kpi-avg').textContent = formatCurrency(row['orders.avg_value']);
109
+ document.getElementById('kpi-avg').classList.remove('loading');
110
+ }
111
+
112
+ // Bar chart — revenue by city
113
+ const byCity = await bon.query({
114
+ measures: ['orders.revenue'],
115
+ dimensions: ['orders.city'],
116
+ orderBy: { 'orders.revenue': 'desc' },
117
+ limit: 10,
118
+ });
119
+
120
+ new Chart(document.getElementById('bar-chart'), {
121
+ type: 'bar',
122
+ data: {
123
+ labels: byCity.data.map(d => d['orders.city']),
124
+ datasets: [{
125
+ label: 'Revenue',
126
+ data: byCity.data.map(d => d['orders.revenue']),
127
+ backgroundColor: '#3b82f6',
128
+ borderRadius: 4,
129
+ }],
130
+ },
131
+ options: {
132
+ responsive: true,
133
+ maintainAspectRatio: false,
134
+ plugins: { legend: { display: false } },
135
+ scales: {
136
+ y: {
137
+ ticks: { callback: v => formatCurrency(v) },
138
+ grid: { color: '#27272a' },
139
+ },
140
+ x: { grid: { display: false } },
141
+ },
142
+ },
143
+ });
144
+
145
+ // Line chart — revenue trend
146
+ const trend = await bon.query({
147
+ measures: ['orders.revenue'],
148
+ timeDimension: {
149
+ dimension: 'orders.created_at',
150
+ granularity: 'month',
151
+ dateRange: 'last 12 months',
152
+ },
153
+ });
154
+
155
+ new Chart(document.getElementById('line-chart'), {
156
+ type: 'line',
157
+ data: {
158
+ labels: trend.data.map(d => {
159
+ const date = new Date(d['orders.created_at']);
160
+ return date.toLocaleDateString('en-US', { month: 'short', year: '2-digit' });
161
+ }),
162
+ datasets: [{
163
+ label: 'Revenue',
164
+ data: trend.data.map(d => d['orders.revenue']),
165
+ borderColor: '#3b82f6',
166
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
167
+ fill: true,
168
+ tension: 0.3,
169
+ pointRadius: 3,
170
+ }],
171
+ },
172
+ options: {
173
+ responsive: true,
174
+ maintainAspectRatio: false,
175
+ plugins: { legend: { display: false } },
176
+ scales: {
177
+ y: {
178
+ ticks: { callback: v => formatCurrency(v) },
179
+ grid: { color: '#27272a' },
180
+ },
181
+ x: { grid: { display: false } },
182
+ },
183
+ },
184
+ });
185
+ } catch (err) {
186
+ showError('Failed to load dashboard: ' + err.message);
187
+ }
188
+ })();
189
+ </script>
190
+ </body>
191
+ </html>
192
+ ```
193
+
194
+ ## Chart types
195
+
196
+ ### Bar chart
197
+
198
+ ```javascript
199
+ new Chart(ctx, {
200
+ type: 'bar',
201
+ data: {
202
+ labels: data.map(d => d['view.dimension']),
203
+ datasets: [{
204
+ label: 'Revenue',
205
+ data: data.map(d => d['view.measure']),
206
+ backgroundColor: '#3b82f6',
207
+ borderRadius: 4,
208
+ }],
209
+ },
210
+ options: {
211
+ responsive: true,
212
+ maintainAspectRatio: false,
213
+ plugins: { legend: { display: false } },
214
+ },
215
+ });
216
+ ```
217
+
218
+ ### Horizontal bar chart
219
+
220
+ ```javascript
221
+ new Chart(ctx, {
222
+ type: 'bar',
223
+ data: { /* same as above */ },
224
+ options: {
225
+ indexAxis: 'y',
226
+ responsive: true,
227
+ maintainAspectRatio: false,
228
+ },
229
+ });
230
+ ```
231
+
232
+ ### Line chart
233
+
234
+ ```javascript
235
+ new Chart(ctx, {
236
+ type: 'line',
237
+ data: {
238
+ labels: data.map(d => new Date(d['view.date']).toLocaleDateString()),
239
+ datasets: [{
240
+ label: 'Revenue',
241
+ data: data.map(d => d['view.measure']),
242
+ borderColor: '#3b82f6',
243
+ tension: 0.3,
244
+ }],
245
+ },
246
+ });
247
+ ```
248
+
249
+ ### Area chart (filled line)
250
+
251
+ ```javascript
252
+ datasets: [{
253
+ label: 'Revenue',
254
+ data: data.map(d => d['view.measure']),
255
+ borderColor: '#3b82f6',
256
+ backgroundColor: 'rgba(59, 130, 246, 0.1)',
257
+ fill: true,
258
+ }]
259
+ ```
260
+
261
+ ### Pie / doughnut chart
262
+
263
+ ```javascript
264
+ new Chart(ctx, {
265
+ type: 'doughnut', // or 'pie'
266
+ data: {
267
+ labels: data.map(d => d['view.dimension']),
268
+ datasets: [{
269
+ data: data.map(d => d['view.measure']),
270
+ backgroundColor: ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6'],
271
+ }],
272
+ },
273
+ });
274
+ ```
275
+
276
+ ### Multi-series line chart
277
+
278
+ ```javascript
279
+ // Query with a grouping dimension
280
+ const { data } = await bon.query({
281
+ measures: ['orders.revenue'],
282
+ dimensions: ['orders.city'],
283
+ timeDimension: { dimension: 'orders.created_at', granularity: 'month' },
284
+ });
285
+
286
+ // Group data by city
287
+ const cities = [...new Set(data.map(d => d['orders.city']))];
288
+ const dates = [...new Set(data.map(d => d['orders.created_at']))];
289
+
290
+ new Chart(ctx, {
291
+ type: 'line',
292
+ data: {
293
+ labels: dates.map(d => new Date(d).toLocaleDateString()),
294
+ datasets: cities.map((city, i) => ({
295
+ label: city,
296
+ data: dates.map(date =>
297
+ data.find(d => d['orders.city'] === city && d['orders.created_at'] === date)?.['orders.revenue'] || 0
298
+ ),
299
+ borderColor: ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6'][i % 5],
300
+ })),
301
+ },
302
+ });
303
+ ```
304
+
305
+ ## Dark mode setup
306
+
307
+ Set Chart.js defaults before creating charts:
308
+
309
+ ```javascript
310
+ Chart.defaults.color = '#a1a1aa'; // label/tick color
311
+ Chart.defaults.borderColor = '#27272a'; // grid line color
312
+ ```
313
+
314
+ ## Color palette
315
+
316
+ Recommended palette for multi-series charts:
317
+
318
+ ```javascript
319
+ const COLORS = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'];
320
+ ```
321
+
322
+ ## See also
323
+
324
+ - [sdk.browser](sdk.browser) — Browser / CDN quickstart
325
+ - [sdk.query-reference](sdk.query-reference) — Full query API
326
+ - [sdk.echarts](sdk.echarts) — ECharts alternative
327
+ - [sdk.apexcharts](sdk.apexcharts) — ApexCharts alternative
@@ -0,0 +1,297 @@
1
+ # ECharts + Bonnard SDK
2
+
3
+ > Build HTML dashboards with Apache ECharts and the Bonnard SDK. No build step required.
4
+
5
+ ECharts offers rich interactivity (tooltips, zoom, legend toggling), built-in dark theme, and extensive chart types. Larger payload than Chart.js (~160KB gzip) but more powerful.
6
+
7
+ ## Starter template
8
+
9
+ Copy this complete HTML file as a starting point. Replace `bon_pk_YOUR_KEY_HERE` with your publishable API key, and update the view/measure/dimension names to match your schema.
10
+
11
+ Use `explore()` to discover available views and fields — see [sdk.query-reference](sdk.query-reference).
12
+
13
+ ```html
14
+ <!DOCTYPE html>
15
+ <html lang="en">
16
+ <head>
17
+ <meta charset="UTF-8">
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ <title>Dashboard</title>
20
+ <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
21
+ <script src="https://cdn.jsdelivr.net/npm/@bonnard/sdk/dist/bonnard.iife.js"></script>
22
+ <style>
23
+ * { margin: 0; padding: 0; box-sizing: border-box; }
24
+ body {
25
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
26
+ background: #09090b; color: #fafafa; padding: 24px;
27
+ }
28
+ h1 { font-size: 24px; font-weight: 600; margin-bottom: 24px; }
29
+ .error { color: #ef4444; background: #1c0a0a; padding: 12px; border-radius: 8px; margin-bottom: 16px; display: none; }
30
+ .kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 32px; }
31
+ .kpi { background: #18181b; border: 1px solid #27272a; border-radius: 12px; padding: 20px; }
32
+ .kpi-label { font-size: 14px; color: #a1a1aa; margin-bottom: 8px; }
33
+ .kpi-value { font-size: 32px; font-weight: 600; }
34
+ .kpi-value.loading { color: #3f3f46; }
35
+ .charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 24px; }
36
+ .chart-card { background: #18181b; border: 1px solid #27272a; border-radius: 12px; padding: 20px; }
37
+ .chart-title { font-size: 16px; font-weight: 500; margin-bottom: 16px; }
38
+ .chart-container { height: 300px; }
39
+ </style>
40
+ </head>
41
+ <body>
42
+ <h1>Dashboard</h1>
43
+ <div id="error" class="error"></div>
44
+
45
+ <div class="kpis">
46
+ <div class="kpi">
47
+ <div class="kpi-label">Revenue</div>
48
+ <div class="kpi-value loading" id="kpi-revenue">--</div>
49
+ </div>
50
+ <div class="kpi">
51
+ <div class="kpi-label">Orders</div>
52
+ <div class="kpi-value loading" id="kpi-orders">--</div>
53
+ </div>
54
+ <div class="kpi">
55
+ <div class="kpi-label">Avg Value</div>
56
+ <div class="kpi-value loading" id="kpi-avg">--</div>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="charts">
61
+ <div class="chart-card">
62
+ <div class="chart-title">Revenue by City</div>
63
+ <div class="chart-container" id="bar-chart"></div>
64
+ </div>
65
+ <div class="chart-card">
66
+ <div class="chart-title">Revenue Trend</div>
67
+ <div class="chart-container" id="line-chart"></div>
68
+ </div>
69
+ </div>
70
+
71
+ <script>
72
+ const bon = Bonnard.createClient({
73
+ apiKey: 'bon_pk_YOUR_KEY_HERE',
74
+ });
75
+
76
+ // --- Helpers ---
77
+ function showError(msg) {
78
+ const el = document.getElementById('error');
79
+ el.textContent = msg;
80
+ el.style.display = 'block';
81
+ }
82
+
83
+ function formatNumber(v) {
84
+ return new Intl.NumberFormat().format(v);
85
+ }
86
+
87
+ function formatCurrency(v) {
88
+ return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(v);
89
+ }
90
+
91
+ // Resize all charts on window resize
92
+ const charts = [];
93
+ window.addEventListener('resize', () => charts.forEach(c => c.resize()));
94
+
95
+ // --- Load data ---
96
+ (async () => {
97
+ try {
98
+ // KPIs
99
+ const kpis = await bon.query({
100
+ measures: ['orders.revenue', 'orders.count', 'orders.avg_value'],
101
+ });
102
+ if (kpis.data.length > 0) {
103
+ const row = kpis.data[0];
104
+ document.getElementById('kpi-revenue').textContent = formatCurrency(row['orders.revenue']);
105
+ document.getElementById('kpi-revenue').classList.remove('loading');
106
+ document.getElementById('kpi-orders').textContent = formatNumber(row['orders.count']);
107
+ document.getElementById('kpi-orders').classList.remove('loading');
108
+ document.getElementById('kpi-avg').textContent = formatCurrency(row['orders.avg_value']);
109
+ document.getElementById('kpi-avg').classList.remove('loading');
110
+ }
111
+
112
+ // Bar chart — revenue by city
113
+ const byCity = await bon.query({
114
+ measures: ['orders.revenue'],
115
+ dimensions: ['orders.city'],
116
+ orderBy: { 'orders.revenue': 'desc' },
117
+ limit: 10,
118
+ });
119
+
120
+ const barChart = echarts.init(document.getElementById('bar-chart'), 'dark');
121
+ charts.push(barChart);
122
+ barChart.setOption({
123
+ tooltip: { trigger: 'axis', formatter: p => `${p[0].name}: ${formatCurrency(p[0].value)}` },
124
+ xAxis: { type: 'category', data: byCity.data.map(d => d['orders.city']) },
125
+ yAxis: { type: 'value', axisLabel: { formatter: v => formatCurrency(v) } },
126
+ series: [{
127
+ type: 'bar',
128
+ data: byCity.data.map(d => d['orders.revenue']),
129
+ itemStyle: { color: '#3b82f6', borderRadius: [4, 4, 0, 0] },
130
+ }],
131
+ grid: { left: 80, right: 20, top: 20, bottom: 40 },
132
+ });
133
+
134
+ // Line chart — revenue trend
135
+ const trend = await bon.query({
136
+ measures: ['orders.revenue'],
137
+ timeDimension: {
138
+ dimension: 'orders.created_at',
139
+ granularity: 'month',
140
+ dateRange: 'last 12 months',
141
+ },
142
+ });
143
+
144
+ const lineChart = echarts.init(document.getElementById('line-chart'), 'dark');
145
+ charts.push(lineChart);
146
+ lineChart.setOption({
147
+ tooltip: { trigger: 'axis', formatter: p => `${p[0].name}: ${formatCurrency(p[0].value)}` },
148
+ xAxis: {
149
+ type: 'category',
150
+ data: trend.data.map(d => {
151
+ const date = new Date(d['orders.created_at']);
152
+ return date.toLocaleDateString('en-US', { month: 'short', year: '2-digit' });
153
+ }),
154
+ },
155
+ yAxis: { type: 'value', axisLabel: { formatter: v => formatCurrency(v) } },
156
+ series: [{
157
+ type: 'line',
158
+ data: trend.data.map(d => d['orders.revenue']),
159
+ smooth: true,
160
+ itemStyle: { color: '#3b82f6' },
161
+ areaStyle: { color: 'rgba(59, 130, 246, 0.15)' },
162
+ }],
163
+ grid: { left: 80, right: 20, top: 20, bottom: 40 },
164
+ });
165
+ } catch (err) {
166
+ showError('Failed to load dashboard: ' + err.message);
167
+ }
168
+ })();
169
+ </script>
170
+ </body>
171
+ </html>
172
+ ```
173
+
174
+ ## Chart types
175
+
176
+ ### Bar chart
177
+
178
+ ```javascript
179
+ const chart = echarts.init(el, 'dark');
180
+ chart.setOption({
181
+ xAxis: { type: 'category', data: data.map(d => d['view.dimension']) },
182
+ yAxis: { type: 'value' },
183
+ series: [{ type: 'bar', data: data.map(d => d['view.measure']) }],
184
+ });
185
+ ```
186
+
187
+ ### Horizontal bar chart
188
+
189
+ ```javascript
190
+ chart.setOption({
191
+ xAxis: { type: 'value' },
192
+ yAxis: { type: 'category', data: data.map(d => d['view.dimension']) },
193
+ series: [{ type: 'bar', data: data.map(d => d['view.measure']) }],
194
+ });
195
+ ```
196
+
197
+ ### Line chart
198
+
199
+ ```javascript
200
+ chart.setOption({
201
+ xAxis: { type: 'category', data: labels },
202
+ yAxis: { type: 'value' },
203
+ series: [{ type: 'line', data: values, smooth: true }],
204
+ });
205
+ ```
206
+
207
+ ### Area chart
208
+
209
+ ```javascript
210
+ series: [{
211
+ type: 'line',
212
+ data: values,
213
+ smooth: true,
214
+ areaStyle: { color: 'rgba(59, 130, 246, 0.15)' },
215
+ }]
216
+ ```
217
+
218
+ ### Pie chart
219
+
220
+ ```javascript
221
+ chart.setOption({
222
+ tooltip: { trigger: 'item' },
223
+ series: [{
224
+ type: 'pie',
225
+ radius: ['40%', '70%'], // doughnut — use '60%' for full pie
226
+ data: data.map(d => ({
227
+ name: d['view.dimension'],
228
+ value: d['view.measure'],
229
+ })),
230
+ }],
231
+ });
232
+ ```
233
+
234
+ ### Multi-series line chart
235
+
236
+ ```javascript
237
+ const cities = [...new Set(data.map(d => d['orders.city']))];
238
+ const dates = [...new Set(data.map(d => d['orders.created_at']))];
239
+
240
+ chart.setOption({
241
+ tooltip: { trigger: 'axis' },
242
+ legend: { data: cities },
243
+ xAxis: { type: 'category', data: dates.map(d => new Date(d).toLocaleDateString()) },
244
+ yAxis: { type: 'value' },
245
+ series: cities.map(city => ({
246
+ name: city,
247
+ type: 'line',
248
+ smooth: true,
249
+ data: dates.map(date =>
250
+ data.find(d => d['orders.city'] === city && d['orders.created_at'] === date)?.['orders.revenue'] || 0
251
+ ),
252
+ })),
253
+ });
254
+ ```
255
+
256
+ ## Dark mode
257
+
258
+ ECharts has a built-in dark theme — pass `'dark'` as the second argument to `echarts.init()`:
259
+
260
+ ```javascript
261
+ const chart = echarts.init(document.getElementById('chart'), 'dark');
262
+ ```
263
+
264
+ The dark theme sets appropriate background, text, axis, and tooltip colors automatically.
265
+
266
+ ## Resize handling
267
+
268
+ ECharts charts don't auto-resize. Add a resize listener:
269
+
270
+ ```javascript
271
+ const charts = [];
272
+
273
+ // After creating each chart:
274
+ charts.push(chart);
275
+
276
+ // Global resize handler:
277
+ window.addEventListener('resize', () => charts.forEach(c => c.resize()));
278
+ ```
279
+
280
+ ## Color palette
281
+
282
+ ```javascript
283
+ const COLORS = ['#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'];
284
+
285
+ // Apply to a series:
286
+ series: data.map((d, i) => ({
287
+ // ...
288
+ itemStyle: { color: COLORS[i % COLORS.length] },
289
+ }))
290
+ ```
291
+
292
+ ## See also
293
+
294
+ - [sdk.browser](sdk.browser) — Browser / CDN quickstart
295
+ - [sdk.query-reference](sdk.query-reference) — Full query API
296
+ - [sdk.chartjs](sdk.chartjs) — Chart.js alternative (smaller payload)
297
+ - [sdk.apexcharts](sdk.apexcharts) — ApexCharts alternative