---
url: https://chartbuddy.io/embed/docs/api.md
---
# Insight API

```js
import {
  Insight,
  version,
} from '@chartbuddy.io/embed';
```

## Constructor

```js
const insight = new Insight(target, options?);
```

### `target`

CSS selector string or `HTMLElement`.

### `options`

| Option | Type | Default | Description |
|---|---|---|---|
| `chartData` | `object` | – | Partial or full chart config ([schema](/api/chart-data)) |
| `instanceId` | `string` | random UUID | Stable id for `getInsights()`. Must be unique on the page — duplicates **throw**. |
| `editable` | `boolean` | `false` | Start in editor mode |
| `allowEdit` | `boolean` | `true` | When `false`, hide Edit on the view ball and no-op `enterEditMode()`. Download / Drag still work. Ignored when locked. |
| `editSession` | `'toggle'` | `'locked'` | `'toggle'` | `'locked'` = always edit (no Done / morph chrome). Implies edit boot. |
| `host` | `object` | — | Host chrome hooks — see [Host overrides](#host-overrides) |
| `assetBase` | `string` | — | Only for multi-file loader; ignore with single-file |

## Instance

| Member | Description |
|---|---|
| `ready` | `Promise` — resolves when mounted |
| `mode` | `'view'` | `'edit'` |
| `editSession` | `'toggle'` | `'locked'` |
| `allowEdit` | `boolean` — whether Edit is offered |
| `chart` | Engine chart instance |
| `instanceId` | Stable id for this mount |
| `setChartData(cd)` | Merge a full or **partial** config and redraw (`chartType` optional) |
| `setData(seriesData)` | Refresh only the data grid — keeps type and formatting |
| `update(patch?)` | Partial patch + redraw; no argument = redraw only |
| `getChartData()` | Full `cd` snapshot |
| `on(event, handler)` | Subscribe to `ready` | `mode` | `change` (returns unsubscribe) |
| `off(event, handler)` | Remove a handler |
| `isDirty()` | `true` when the chart changed since boot / last Done |
| `getRevision()` | Monotonic edit counter |
| `toPngBlob()` / `toPngBase64()` | PNG bytes/base64 without a Save dialog |
| `downloadPng()` | Download PNG (human Save dialog) |
| `exportConfig()` | Download current `chartData` as JSON |
| `enterEditMode()` | View → edit (no-op when locked or `allowEdit: false`) |
| `exitEditMode()` | Edit → view (no-op when locked) |
| `focus()` | Focus the insight |
| `destroy()` | Tear down |

## Host overrides

Optional `options.host` hooks for embedding inside your own chrome:

| Hook | Description |
|---|---|
| `getToolbarPlacement()` | Where the formatting toolbar lives: `'widget'` (default embed edit — morph rail), `'float'`, or `'dock'`. |
| `getToolbarContainer()` | Mount point for the toolbar (selector or element). Ignored when placement is `'widget'` (modules mount in the morph). |
| `positionToolbar(toolbar, target)` | Float-only XY override. Ignored for `'dock'` / `'widget'`. |
| `getPopupContainer()` | Where portaled menus mount (selector or element). Default: `document.body`. Use your dialog root when the editor sits in a high-z overlay. |
| `startDragging(event)` | Forward chart-background drag to host window chrome. |

```js
new Insight('#chart', {
  editable: true,
  editSession: 'locked',
  host: {
    getToolbarPlacement: () => ({ mode: 'dock', side: 'right' }),
    getToolbarContainer: () => '#my-toolbar-rail',
    getPopupContainer: () => '#my-dialog',
  },
});
```

## Partial updates

```js
await insight.ready;

// Data only — Chart.js-shaped
insight.setData([
  ['', 'Q1', 'Q2'],
  ['Revenue', 100, 120],
]);

// Any partial patch
insight.update({ title: { text: 'FY26' } });

// Redraw without changing config
insight.update();

// setChartData also accepts partials — chartType is not required
insight.setChartData({ seriesData: [/* … */] });
```

## Events

```js
insight.on('ready', () => { /* booted */ });
insight.on('mode', (mode) => { /* 'view' | 'edit' */ });
insight.on('change', (cd) => { /* after meaningful edits */ });

insight.isDirty();     // since boot / last Done checkpoint
insight.getRevision(); // increments on every meaningful change
```

`change` fires after programmatic `setChartData` / `setData` / `update`, live edits in edit mode, and Done.

## Validation

`new Insight({ chartData })`, `setChartData`, `setData`, and `update` validate input at the API boundary:

| Input | Behaviour |
|---|---|
| Unknown `chartType` (e.g. `bubbleChart3D`) | **Throws** (with a did-you-mean hint) |
| Wrong field type / enum / range | **Throws** (lists every path) |
| Foreign option bag (`pie` + `bar: {…}`) | **Throws** |
| Ragged `seriesData` (unequal row lengths) | **Warns**, still accepts |
| Duplicate `instanceId` on the page | **Throws** |
| Unknown keys | Always allowed (forward-compatible) |

Throws are `ChartDataValidationError`, which carries the problems as structured
data — and the same checks are callable directly, so you can validate before you
mount. See [Validation](/quality-assurance/validation).

```js
import { validateChartData } from '@chartbuddy.io/embed';

const { valid, errors } = validateChartData(candidate);
if (!valid) console.log(errors[0].path, errors[0].code, errors[0].suggestion);
```

## PNG / export

```js
await insight.ready;
const png = await insight.toPngBase64(); // default background #ffffff
await insight.downloadPng();
insight.exportConfig();
```

PNG download and drag-to-slide are also available from the view-mode hover ball.
