@chartbuddy.io/embed 1.7.52

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/llms.txt ADDED
@@ -0,0 +1,341 @@
1
+ # @chartbuddy.io/embed — LLM / Claude usage guide
2
+
3
+ Package: @chartbuddy.io/embed
4
+ Public API: Insight (new Insight()), getInsights, snapshotInsights,
5
+ clearPersistedChart, version
6
+ Purpose: Mount a ChartBuddy chart in HTML. Default is view-only (hover ball:
7
+ Download PNG / Drag to slide / Edit). Pass editable:true for the full
8
+ interactive editor (canvas + formatting toolbar).
9
+ NOT Chart.js. Spreadsheet (Handsontable) is NOT included.
10
+ No ChartBuddy account / chart ID required — pass data via options.chartData.
11
+
12
+ Stable docs URLs (bookmark / fetch these):
13
+ - https://unpkg.com/@chartbuddy.io/embed/llms.txt
14
+ - https://cb-acc.xyz/docs/
15
+ - https://unpkg.com/@chartbuddy.io/embed/README.md
16
+
17
+ Every JS artifact (package root stub, .single.mjs, multi loader) opens with a
18
+ comment header naming the exports and a minimal Insight example.
19
+
20
+ ## Claude Visualizer / inline artifacts (REQUIRED path)
21
+
22
+ Import the PACKAGE ROOT (readable API stub → self-contained single-file build):
23
+
24
+ ```js
25
+ import { Insight } from 'https://unpkg.com/@chartbuddy.io/embed';
26
+ ```
27
+
28
+ Equivalents (also fine — each file self-describes in its first lines):
29
+ - https://unpkg.com/@chartbuddy.io/embed/chartbuddy-embed.api.mjs
30
+ - https://unpkg.com/@chartbuddy.io/embed/chartbuddy-embed.single.mjs
31
+ - https://cdn.jsdelivr.net/npm/@chartbuddy.io/embed
32
+ - https://cb-acc.xyz/api/embed/chartbuddy-embed.single.mjs
33
+
34
+ Rules:
35
+ - Create with **new Insight()** (not createChart / renderChart / mountEditor / mountChart).
36
+ - Do NOT pass assetBase on this path.
37
+ - Do NOT import chartbuddy-embed.mjs (multi-file) inside Claude Visualizer.
38
+ - Do NOT expect a spreadsheet UI.
39
+ - Default is view-only; pass editable: true for editor chrome / formatting UI.
40
+ - Full chartData schema is below; also in README.md on the same package.
41
+
42
+
43
+ ### Copy-paste HTML artifact
44
+
45
+ ```html
46
+ <!DOCTYPE html>
47
+ <html>
48
+ <head>
49
+ <meta charset="utf-8" />
50
+ <title>ChartBuddy</title>
51
+ <style>
52
+ html, body { margin: 0; height: 100%; }
53
+ #chart { width: 100%; height: 100%; min-height: 480px; }
54
+ </style>
55
+ </head>
56
+ <body>
57
+ <div id="chart"></div>
58
+ <script type="module">
59
+ import { Insight } from 'https://unpkg.com/@chartbuddy.io/embed';
60
+
61
+ // Default: view-only + ChartBuddy hover ball (Download / Export / Edit)
62
+ const insight = new Insight('#chart', {
63
+ instanceId: 'main',
64
+ persist: false,
65
+ chartData: {
66
+ chartType: 'clusteredBar',
67
+ isDataTransposed: true,
68
+ seriesData: [
69
+ ['', 'Q1', 'Q2', 'Q3', 'Q4'],
70
+ ['Revenue', 100, 112, 125, 140],
71
+ ['Costs', 60, 66, 70, 78],
72
+ ],
73
+ title: { visible: true, text: 'Revenue vs Costs' },
74
+ subtitle: { visible: false, text: '' },
75
+ },
76
+ });
77
+
78
+ await insight.ready;
79
+ // Full editor: new Insight('#chart', { editable: true, chartData: … })
80
+ </script>
81
+ </body>
82
+ </html>
83
+ ```
84
+
85
+ ### API
86
+
87
+ ```js
88
+ import {
89
+ Insight,
90
+ getInsights,
91
+ snapshotInsights,
92
+ clearPersistedChart,
93
+ version,
94
+ } from 'https://unpkg.com/@chartbuddy.io/embed';
95
+
96
+ const insight = new Insight(target, options?);
97
+ // target: CSS selector string OR HTMLElement
98
+ // options.chartData?: ChartBuddy cd object (schema below)
99
+ // options.instanceId?: string // STABLE id for agents + persist (recommended)
100
+ // options.editable?: boolean (default false → view mode)
101
+ // options.toolbar?: boolean (edit mode only; default false)
102
+ // options.persist?: boolean (default false; needs stable instanceId to reload)
103
+ // options.assetBase?: IGNORE for single-file
104
+
105
+ await insight.ready;
106
+ insight.instanceId
107
+ insight.mode // 'view' | 'edit'
108
+ insight.chart
109
+ insight.setChartData(cd) // partial OK — chartType optional
110
+ insight.setData(seriesData) // data-only refresh
111
+ insight.update(patch?) // partial patch; no arg = redraw
112
+ insight.getChartData() // full cd snapshot — best source for a complete schema example
113
+ insight.on('ready' | 'mode' | 'change', handler) // returns unsubscribe
114
+ insight.off(event, handler)
115
+ insight.isDirty()
116
+ insight.getRevision()
117
+ insight.toPngBase64() // PNG base64 (NO download); default background #ffffff
118
+ insight.toPngBase64({ background: '#0f172a' }) // custom opaque underlay
119
+ insight.toPngBase64({ background: null }) // transparent (Slides-style)
120
+ insight.toPngBlob() // PNG as Blob (NO download); same background defaults
121
+ insight.downloadPng() // human Save dialog; default transparent
122
+ insight.downloadPng({ background: '#ffffff' }) // opaque human download
123
+ insight.exportConfig() // human JSON download
124
+ insight.enterEditMode() // view → full editor
125
+ insight.exitEditMode() // edit → view (also ball → Done); checkpoints if persist
126
+ insight.focus()
127
+ insight.destroy()
128
+ getInsights() // { [instanceId]: Insight }
129
+ await snapshotInsights() // config-only for all mounts
130
+ await snapshotInsights({ png: true }) // + pngBase64 (default white bg)
131
+ await snapshotInsights({ png: true, background: '#111827' })
132
+ clearPersistedChart(instanceId?)
133
+ version
134
+ validateChartData(cd, options?) // → { valid, issues, errors, warnings }; NEVER throws
135
+ assertValidChartData(cd, context?, options?) // throws ChartDataValidationError
136
+ formatValidationIssues(issues, context?) // human-readable string
137
+ // Also: window.__CHARTBUDDY_INSIGHTS__ === getInsights()
138
+ ```
139
+
140
+ Bundler: `import { Insight } from '@chartbuddy.io/embed';`
141
+ React: `import { InsightChart, useInsight } from '@chartbuddy.io/embed/react';`
142
+ Vue 3: `import { InsightChart, useInsight } from '@chartbuddy.io/embed/vue';`
143
+ Angular / Svelte / Solid / plain HTML — custom element, no peer dependency:
144
+ `import '@chartbuddy.io/embed/element';` → `<chartbuddy-insight>`
145
+ set `.chartData` as a PROPERTY; events are CustomEvents (ready/change/mode/error)
146
+ All bindings mount once, patch on chartData change, and destroy on unmount.
147
+
148
+ ## Agent observation (round-trip / visual QA)
149
+
150
+ Browsers cannot rewrite the HTML file. Agents (Cursor, Claude, CI) can —
151
+ ChartBuddy returns structured data; the host agent writes files.
152
+
153
+ ### Recommended loop
154
+
155
+ 1. Author HTML with **stable `instanceId`** per mount.
156
+ 2. Human edits in-browser (ball → Edit → Done).
157
+ 3. Agent observes:
158
+
159
+ ```js
160
+ // CDP / Runtime.evaluate — after page has mounted Insights:
161
+ const insights = window.__CHARTBUDDY_INSIGHTS__;
162
+ const insight = insights['revenue'];
163
+ const cd = insight.getChartData();
164
+ // Default opaque white underlay — pass background for other colors:
165
+ const png = await insight.toPngBase64(); // raw base64, no data: prefix
166
+ // const png = await insight.toPngBase64({ background: '#0f172a' });
167
+ ```
168
+
169
+ 4. Agent writes `deck.charts.json` / `exports/revenue.png` into the workspace.
170
+ 5. LLM diffs JSON vs prior seed; updates narrative or re-seeds HTML.
171
+
172
+ ### PNG background note
173
+
174
+ Export strips the live SVG `#chartBackground` so Slides overlays stay transparent.
175
+ `chartData.backgroundColor` alone does **not** bake into PNGs. Use
176
+ `toPngBase64({ background })` / `toPngBlob({ background })` instead.
177
+ Agent helpers default to `#ffffff`. Pass any CSS color, or `null`/`'transparent'`
178
+ for a transparent PNG.
179
+
180
+ ### Rules for agents
181
+
182
+ - Prefer **`toPngBase64` / `getChartData`** over screenshots or `downloadPng`.
183
+ - Use **`background`** on PNG helpers for visual QA (do not rely on `cd.backgroundColor`).
184
+ - Always pass **`instanceId`** on multi-chart pages (and for `persist: true`).
185
+ - Snapshot **config-only** by default; request PNG one chart at a time.
186
+ - Do **not** expect HTML to rewrite itself after edits.
187
+ - Do **not** expect `downloadPng` / `exportConfig` downloads to reach the agent.
188
+
189
+ ### Optional sidecar convention (agent-written, not ChartBuddy)
190
+
191
+ ```text
192
+ deck.html
193
+ deck.charts.json // all chartData from getInsights / snapshotInsights
194
+ exports/
195
+ revenue.png
196
+ ```
197
+
198
+ ## chartData (`cd`) schema
199
+
200
+ Partial `chartData` is merged over defaults. Nested keys `title`, `subtitle`,
201
+ `legend`, `axes`, `canvas`, `footnote` are deep-merged.
202
+
203
+ ### Required
204
+ - `chartType` (string)
205
+ - `seriesData` (2D array)
206
+
207
+ ### Recommended
208
+ - `isDataTransposed: true`
209
+ - `title: { visible: true, text: '…' }`
210
+ - `subtitle: { visible: false, text: '' }` // hide default placeholder subtitle
211
+ - `legend: { visible: true, colors?: string[] }` // palette is legend.colors
212
+ - `backgroundColor`, `orientation` (`"vertical"` | `"horizontal"`)
213
+
214
+ ### chartType values
215
+ CHARTBUDDY:GENERATED-CHART-TYPES:BEGIN
216
+ `area` | `area100` | `barMekko` | `clusteredBar` | `combo` | `line`
217
+ `mekko` | `pie` | `scatter` | `stackedBar` | `stackedBar100` | `waterfall`
218
+
219
+ Aliases: `bubble`→`scatter`, `donut`→`pie`.
220
+
221
+ Beta: `barMekko`.
222
+ CHARTBUDDY:GENERATED-CHART-TYPES:END
223
+
224
+ ### Validation (read this before generating a config)
225
+
226
+ `new Insight({ chartData })` and `setChartData()` / `setData()` / `update()`
227
+ validate input and THROW on invalid data, listing the path of every problem.
228
+ Checked: chartType (with a did-you-mean hint), field types, enums, numeric
229
+ ranges, the seriesData grid and its per-type row/column minimums, and
230
+ option-bag/chartType agreement — `bar` options on a `pie` chart are rejected.
231
+ Ragged seriesData (unequal row lengths) WARNS but does not throw. Duplicate
232
+ instanceId on the same page THROWS. Unknown keys are never an error.
233
+
234
+ Partial updates are first-class: `setData(seriesData)`, `update(patch)`, and
235
+ `setChartData(partial)` all keep the current chart type when chartType is omitted.
236
+
237
+ Events: `on('ready'|'mode'|'change', handler)`, `isDirty()`, `getRevision()`.
238
+
239
+ Example failure:
240
+ `pie.innerRadiusRatio: 5 is above the maximum of 1`
241
+
242
+ BEST PRACTICE — check before you mount, instead of using a blank chart as the
243
+ error signal. `validateChartData(cd)` never throws and returns every problem:
244
+
245
+ ```js
246
+ const { valid, errors, warnings } = validateChartData(cd);
247
+ // each issue: { path, code, message, severity, expected?, received?, allowed?, suggestion? }
248
+ ```
249
+
250
+ Branch on `code`, NEVER on `message` (messages may be reworded in any release):
251
+ unknown-chart-type → use issue.suggestion, or pick from issue.allowed
252
+ not-in-enum → use issue.suggestion, or pick from issue.allowed
253
+ wrong-type → coerce to issue.expected (also catches NaN / Infinity)
254
+ out-of-range → clamp to the bound in issue.expected
255
+ series-data-shape → reshape; expected names the row/column minimum
256
+ ragged-series-data → WARNING; pad the short rows
257
+ foreign-option-bag → move options into the bag named in issue.suggestion
258
+ empty-patch → include chartType and/or seriesData
259
+ not-an-object → pass an object
260
+
261
+ Repair loop (no second model call needed for typos):
262
+ ```js
263
+ let cd = generated;
264
+ for (const issue of validateChartData(cd).errors) {
265
+ if (issue.suggestion) cd = setAtPath(cd, issue.path, issue.suggestion);
266
+ }
267
+ ```
268
+
269
+ If you mount without checking, the throw is a `ChartDataValidationError` carrying
270
+ the same objects: `err.errors`, `err.warnings`, `err.issues`, `JSON.stringify(err)`.
271
+
272
+ The machine-readable form of these rules is chart-schema.json, which can be used
273
+ to constrain structured output directly.
274
+
275
+ ### seriesData layouts
276
+
277
+ Bar / line / area / stacked (rows = series):
278
+ ```js
279
+ [
280
+ ['', 'Q1', 'Q2', 'Q3'],
281
+ ['Revenue', 100, 112, 125],
282
+ ['Costs', 60, 66, 70],
283
+ ]
284
+ ```
285
+
286
+ Pie:
287
+ ```js
288
+ [
289
+ ['Category', 'Value'],
290
+ ['North', 45],
291
+ ['South', 30],
292
+ ]
293
+ ```
294
+
295
+ Scatter (no transpose):
296
+ ```js
297
+ [
298
+ ['', 'Metric X', 'Metric Y', 'Size', 'Group'],
299
+ ['Point 1', 10, 15, 8, 'A'],
300
+ ]
301
+ ```
302
+
303
+ ### Full cd
304
+ Prefer `insight.getChartData()` / `insight.exportConfig()`, or author in the editor then snapshot.
305
+ Advanced fields on full exports: `canvas`, `axes`, `annotations`, `multilines`,
306
+ type-specific blocks (`bar`, `line`, `pie`, …), `seriesLabels`, `chartPositionPercentages`.
307
+
308
+ ## Multi-mount dashboard
309
+
310
+ Construct `new Insight()` once per container in the same document. Each instance is isolated.
311
+ Pass a stable `instanceId` per mount so agents can address charts by name.
312
+
313
+ ```js
314
+ new Insight('#a', { instanceId: 'revenue', chartData: … });
315
+ new Insight('#b', { instanceId: 'bridge', chartData: … });
316
+ await snapshotInsights(); // { charts: { revenue: { chartData }, bridge: { … } } }
317
+ ```
318
+
319
+ ## Fallbacks
320
+
321
+ | Situation | Action |
322
+ |-----------|--------|
323
+ | Claude Visualizer / strict script-src | single-file unpkg/jsDelivr |
324
+ | Host blocks large ESM | iframe https://cb-acc.xyz/embed/ |
325
+ | You control page + siblings OK | multi-file chartbuddy-embed.mjs |
326
+
327
+ ## Do not
328
+ - Pass assetBase with single-file
329
+ - Import sibling d3 / webapp-entry / wasm / worker yourself
330
+ - Expect Handsontable / spreadsheet windows
331
+ - Expect `downloadPng` to feed agents (use `toPngBase64`)
332
+ - Expect the browser to rewrite HTML / sidecar files
333
+ - Use for production client-facing apps under the current evaluation LICENSE
334
+
335
+ ## Verification checklist
336
+ - `document.querySelectorAll('svg[id^="chart-svg-"]').length >= 1`
337
+ - `document.querySelectorAll('.spreadsheet-window').length === 0`
338
+ - Default view: no `.webapp-toolbar`; hover → `[data-cb-embed-chrome]`
339
+ - Network: one ChartBuddy JS request (`.single.mjs`) plus maybe fonts
340
+ - Observation: `Object.keys(window.__CHARTBUDDY_INSIGHTS__ || {}).length >= 1`
341
+ - Observation: `typeof insight.toPngBase64 === 'function'` and `getChartData()` returns `chartType`
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@chartbuddy.io/embed",
3
+ "version": "1.7.52",
4
+ "description": "Embed ChartBuddy Insights via new Insight() — default view-only with hover ball (Download/Drag to slide/Edit; Done exits edit); editable:true for the full editor. Claude/Visualizer: import from this package (unpkg → readable API stub → single-file). Read llms.txt. Spreadsheet not included.",
5
+ "type": "module",
6
+ "main": "./chartbuddy-embed.api.mjs",
7
+ "module": "./chartbuddy-embed.api.mjs",
8
+ "types": "./chartbuddy-embed.d.ts",
9
+ "unpkg": "./chartbuddy-embed.api.mjs",
10
+ "jsdelivr": "./chartbuddy-embed.api.mjs",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./chartbuddy-embed.d.ts",
14
+ "import": "./chartbuddy-embed.api.mjs",
15
+ "default": "./chartbuddy-embed.api.mjs"
16
+ },
17
+ "./single": {
18
+ "types": "./chartbuddy-embed.d.ts",
19
+ "import": "./chartbuddy-embed.single.mjs",
20
+ "default": "./chartbuddy-embed.single.global.js"
21
+ },
22
+ "./multi": {
23
+ "types": "./chartbuddy-embed.d.ts",
24
+ "import": "./chartbuddy-embed.mjs",
25
+ "default": "./chartbuddy-embed.global.js"
26
+ },
27
+ "./react": {
28
+ "types": "./react.d.ts",
29
+ "import": "./react.mjs",
30
+ "default": "./react.mjs"
31
+ },
32
+ "./vue": {
33
+ "types": "./vue.d.ts",
34
+ "import": "./vue.mjs",
35
+ "default": "./vue.mjs"
36
+ },
37
+ "./element": {
38
+ "types": "./element.d.ts",
39
+ "import": "./element.mjs",
40
+ "default": "./element.mjs"
41
+ },
42
+ "./llms.txt": "./llms.txt",
43
+ "./chart-schema.json": "./chart-schema.json",
44
+ "./package.json": "./package.json"
45
+ },
46
+ "files": [
47
+ "chartbuddy-embed.api.mjs",
48
+ "chartbuddy-embed.mjs",
49
+ "chartbuddy-embed.global.js",
50
+ "chartbuddy-embed.single.mjs",
51
+ "chartbuddy-embed.single.global.js",
52
+ "chartbuddy-embed.d.ts",
53
+ "react.mjs",
54
+ "react.d.ts",
55
+ "vue.mjs",
56
+ "vue.d.ts",
57
+ "element.mjs",
58
+ "element.d.ts",
59
+ "chart-schema.d.ts",
60
+ "chart-schema.json",
61
+ "webapp-entry.js",
62
+ "webapp-entry.css",
63
+ "ts/**",
64
+ "js/**",
65
+ "labelPlacementAccel-*.wasm",
66
+ "README.md",
67
+ "llms.txt",
68
+ "LICENSE"
69
+ ],
70
+ "keywords": [
71
+ "chartbuddy",
72
+ "chart",
73
+ "chart-editor",
74
+ "data-visualization",
75
+ "embed",
76
+ "editable",
77
+ "Insight",
78
+ "claude",
79
+ "react",
80
+ "vue",
81
+ "angular",
82
+ "web-components"
83
+ ],
84
+ "peerDependencies": {
85
+ "react": ">=18",
86
+ "vue": ">=3"
87
+ },
88
+ "peerDependenciesMeta": {
89
+ "react": {
90
+ "optional": true
91
+ },
92
+ "vue": {
93
+ "optional": true
94
+ }
95
+ },
96
+ "homepage": "https://chartbuddy.io",
97
+ "license": "SEE LICENSE IN LICENSE",
98
+ "publishConfig": {
99
+ "access": "public"
100
+ }
101
+ }
package/react.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Type declarations for `@chartbuddy.io/embed/react`.
3
+ *
4
+ * `react` is a peer dependency — install it yourself. React 18 or 19.
5
+ */
6
+
7
+ import type { CSSProperties, ReactElement, ReactNode, RefObject } from 'react';
8
+ import type {
9
+ ChartData,
10
+ ChartDataInput,
11
+ EmbedMode,
12
+ Insight,
13
+ InsightOptions,
14
+ } from './chartbuddy-embed';
15
+
16
+ /** Insight options that React drives, minus the ones the hook manages itself. */
17
+ export interface UseInsightOptions
18
+ extends Omit<InsightOptions, 'chartData'> {
19
+ /**
20
+ * Chart config to render. Compared **by identity**, not deeply: when this
21
+ * changes, the hook calls `update()` on the live instance rather than
22
+ * remounting. Build it with `useMemo` (or keep it in state) so an inline
23
+ * object literal does not fire an update on every render.
24
+ */
25
+ chartData?: ChartDataInput;
26
+ /** Called once the insight has finished booting. */
27
+ onReady?: (insight: Insight) => void;
28
+ /** Called after any meaningful config change, including live edits. */
29
+ onChange?: (chartData: ChartData | null) => void;
30
+ /** Called when the insight switches between view and edit. */
31
+ onModeChange?: (mode: EmbedMode) => void;
32
+ }
33
+
34
+ export interface UseInsightResult {
35
+ /** Attach to the element that should hold the chart. Size it yourself. */
36
+ ref: RefObject<HTMLDivElement | null>;
37
+ /** The live instance, or null before mount / after unmount. */
38
+ insight: Insight | null;
39
+ /** True once `insight.ready` has resolved. */
40
+ ready: boolean;
41
+ /**
42
+ * The last validation or boot failure, or null. Invalid `chartData` surfaces
43
+ * here as a `ChartDataValidationError` — read `error.issues` for the paths.
44
+ */
45
+ error: Error | null;
46
+ }
47
+
48
+ /**
49
+ * Mount an Insight into a ref'd element and keep it alive across renders.
50
+ *
51
+ * Safe under StrictMode: the throwaway first mount is torn down cleanly and its
52
+ * late `ready` resolution is ignored. Changing `instanceId`, `editable`,
53
+ * `toolbar`, `persist`, `assetBase`, or `hostOverrides` remounts the chart;
54
+ * changing `chartData` patches it in place.
55
+ *
56
+ * @example
57
+ * const chartData = useMemo(() => ({ chartType: 'line', seriesData: grid }), [grid]);
58
+ * const { ref, insight, ready } = useInsight({ chartData, editable: true });
59
+ * return <div ref={ref} style={{ height: 400 }} />;
60
+ */
61
+ export function useInsight(options?: UseInsightOptions): UseInsightResult;
62
+
63
+ /** Render-prop argument for `<InsightChart>`'s function children. */
64
+ export interface InsightChartRenderProps {
65
+ insight: Insight | null;
66
+ ready: boolean;
67
+ error: Error | null;
68
+ }
69
+
70
+ export interface InsightChartProps extends UseInsightOptions {
71
+ /**
72
+ * Called with any validation or boot error. When omitted, errors are thrown
73
+ * during render so a React error boundary handles them — a chart that fails
74
+ * validation should never fail silently.
75
+ */
76
+ onError?: (error: Error) => void;
77
+ className?: string;
78
+ /** The chart fills its container, so give it a height. */
79
+ style?: CSSProperties;
80
+ /** Overlay content, or a render prop receiving `{ insight, ready, error }`. */
81
+ children?: ReactNode | ((props: InsightChartRenderProps) => ReactNode);
82
+ }
83
+
84
+ /**
85
+ * Declarative single-div wrapper around `useInsight`.
86
+ *
87
+ * @example
88
+ * <InsightChart
89
+ * chartData={chartData}
90
+ * editable
91
+ * onChange={setChartData}
92
+ * style={{ height: 400 }}
93
+ * />
94
+ */
95
+ export function InsightChart(props: InsightChartProps): ReactElement;
96
+
97
+ export default InsightChart;