@sqlrooms/vega 0.29.0-rc.2 → 0.29.0-rc.4
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/README.md +23 -0
- package/dist/ChartImageForMarkdownTool.d.ts +84 -0
- package/dist/ChartImageForMarkdownTool.d.ts.map +1 -0
- package/dist/ChartImageForMarkdownTool.js +287 -0
- package/dist/ChartImageForMarkdownTool.js.map +1 -0
- package/dist/VegaChartToolResult.d.ts +2 -2
- package/dist/VegaChartToolResult.d.ts.map +1 -1
- package/dist/VegaChartToolResult.js +2 -2
- package/dist/VegaChartToolResult.js.map +1 -1
- package/dist/VegaLiteArrowChart.d.ts.map +1 -1
- package/dist/VegaLiteArrowChart.js +7 -3
- package/dist/VegaLiteArrowChart.js.map +1 -1
- package/dist/editor/VegaChartDisplay.js +1 -1
- package/dist/editor/VegaChartDisplay.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +25 -24
- package/dist/VegaLiteChart.d.ts +0 -44
- package/dist/VegaLiteChart.d.ts.map +0 -1
- package/dist/VegaLiteChart.js +0 -134
- package/dist/VegaLiteChart.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ npm install @sqlrooms/vega @sqlrooms/duckdb @sqlrooms/ui
|
|
|
10
10
|
|
|
11
11
|
- `VegaLiteChart` (simple + compound component API)
|
|
12
12
|
- `createVegaChartTool()` for AI tool workflows
|
|
13
|
+
- `createChartImageForMarkdownTool()` for AI-generated Markdown document image assets
|
|
13
14
|
- `VegaChartToolResult`
|
|
14
15
|
- editor utilities/hooks (`useVegaChartEditor`, `useVegaEditorContext`)
|
|
15
16
|
|
|
@@ -104,6 +105,28 @@ These fields are supplied by the LLM when invoking the tool (not passed into
|
|
|
104
105
|
- `vegaLiteSpec`: Vega-Lite JSON string
|
|
105
106
|
- `reasoning`: explanation shown to users for why this chart/spec was chosen
|
|
106
107
|
|
|
108
|
+
## Markdown document image assets
|
|
109
|
+
|
|
110
|
+
`createChartImageForMarkdownTool(store)` creates an AI-only companion tool that
|
|
111
|
+
renders a Vega chart to SVG or PNG, stores it as an asset on a
|
|
112
|
+
`@sqlrooms/documents` Markdown artifact, and returns a ready-to-insert Markdown
|
|
113
|
+
image link such as:
|
|
114
|
+
|
|
115
|
+
```md
|
|
116
|
+

|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Use this alongside the existing document commands when the assistant needs a
|
|
120
|
+
portable conversation summary with static chart images instead of live SQL-backed
|
|
121
|
+
charts.
|
|
122
|
+
|
|
123
|
+
Chart images default to the light Vega theme with an explicit background so
|
|
124
|
+
exported Markdown renders predictably in GitHub, Obsidian, PDF exports, and
|
|
125
|
+
other document surfaces. When the requested static theme matches the current app
|
|
126
|
+
theme, the background is resolved from the app's Tailwind `--background` token
|
|
127
|
+
and written into the SVG/PNG as a concrete color. The tool also accepts
|
|
128
|
+
`renderTheme: "dark"` and `background` for explicit dark/static export requests.
|
|
129
|
+
|
|
107
130
|
## Example apps
|
|
108
131
|
|
|
109
132
|
- Vega example: https://github.com/sqlrooms/examples/tree/main/vega
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { AiSliceState } from '@sqlrooms/ai-core';
|
|
2
|
+
import type { DocumentsSliceState } from '@sqlrooms/documents';
|
|
3
|
+
import { type DuckDbSliceState } from '@sqlrooms/duckdb';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
export declare const ChartImageForMarkdownToolParameters: z.ZodObject<{
|
|
6
|
+
documentArtifactId: z.ZodString;
|
|
7
|
+
sourceToolCallId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
sqlQuery: z.ZodOptional<z.ZodString>;
|
|
9
|
+
vegaLiteSpec: z.ZodOptional<z.ZodString>;
|
|
10
|
+
format: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
11
|
+
svg: "svg";
|
|
12
|
+
png: "png";
|
|
13
|
+
}>>>;
|
|
14
|
+
assetId: z.ZodOptional<z.ZodString>;
|
|
15
|
+
alt: z.ZodString;
|
|
16
|
+
title: z.ZodOptional<z.ZodString>;
|
|
17
|
+
width: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
18
|
+
height: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
19
|
+
pngScale: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
20
|
+
renderTheme: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
21
|
+
light: "light";
|
|
22
|
+
dark: "dark";
|
|
23
|
+
}>>>;
|
|
24
|
+
background: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>;
|
|
26
|
+
export type ChartImageForMarkdownToolParameters = z.infer<typeof ChartImageForMarkdownToolParameters>;
|
|
27
|
+
export type ChartImageForMarkdownToolOutput = {
|
|
28
|
+
success: boolean;
|
|
29
|
+
details: string;
|
|
30
|
+
documentArtifactId: string;
|
|
31
|
+
assetId?: string;
|
|
32
|
+
markdown?: string;
|
|
33
|
+
mediaType?: 'image/svg+xml' | 'image/png';
|
|
34
|
+
encoding?: 'utf8' | 'base64';
|
|
35
|
+
format?: 'svg' | 'png';
|
|
36
|
+
renderTheme?: 'light' | 'dark';
|
|
37
|
+
background?: string;
|
|
38
|
+
error?: string;
|
|
39
|
+
};
|
|
40
|
+
type StoreLike<S> = {
|
|
41
|
+
getState: () => S;
|
|
42
|
+
};
|
|
43
|
+
type ChartImageToolState = AiSliceState & DuckDbSliceState & DocumentsSliceState;
|
|
44
|
+
export declare function createChartImageForMarkdownTool<S extends ChartImageToolState = ChartImageToolState>(store: StoreLike<S>): import("ai").Tool<{
|
|
45
|
+
documentArtifactId: string;
|
|
46
|
+
format: "svg" | "png";
|
|
47
|
+
alt: string;
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
pngScale: number;
|
|
51
|
+
renderTheme: "light" | "dark";
|
|
52
|
+
sourceToolCallId?: string | undefined;
|
|
53
|
+
sqlQuery?: string | undefined;
|
|
54
|
+
vegaLiteSpec?: string | undefined;
|
|
55
|
+
assetId?: string | undefined;
|
|
56
|
+
title?: string | undefined;
|
|
57
|
+
background?: string | undefined;
|
|
58
|
+
}, {
|
|
59
|
+
success: false;
|
|
60
|
+
details: string;
|
|
61
|
+
documentArtifactId: string;
|
|
62
|
+
error: string;
|
|
63
|
+
assetId?: undefined;
|
|
64
|
+
markdown?: undefined;
|
|
65
|
+
mediaType?: undefined;
|
|
66
|
+
encoding?: undefined;
|
|
67
|
+
format?: undefined;
|
|
68
|
+
renderTheme?: undefined;
|
|
69
|
+
background?: undefined;
|
|
70
|
+
} | {
|
|
71
|
+
success: true;
|
|
72
|
+
details: string;
|
|
73
|
+
documentArtifactId: string;
|
|
74
|
+
assetId: string;
|
|
75
|
+
markdown: string;
|
|
76
|
+
mediaType: "image/svg+xml" | "image/png";
|
|
77
|
+
encoding: "utf8" | "base64";
|
|
78
|
+
format: "svg" | "png";
|
|
79
|
+
renderTheme: "light" | "dark";
|
|
80
|
+
background: string;
|
|
81
|
+
error?: undefined;
|
|
82
|
+
}>;
|
|
83
|
+
export {};
|
|
84
|
+
//# sourceMappingURL=ChartImageForMarkdownTool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartImageForMarkdownTool.d.ts","sourceRoot":"","sources":["../src/ChartImageForMarkdownTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAmB,KAAK,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAIzE,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAStB,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;iBA6C7C,CAAC;AAEJ,MAAM,MAAM,mCAAmC,GAAG,CAAC,CAAC,KAAK,CACvD,OAAO,mCAAmC,CAC3C,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,eAAe,GAAG,WAAW,CAAC;IAC1C,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,KAAK,SAAS,CAAC,CAAC,IAAI;IAClB,QAAQ,EAAE,MAAM,CAAC,CAAC;CACnB,CAAC;AAEF,KAAK,mBAAmB,GAAG,YAAY,GACrC,gBAAgB,GAChB,mBAAmB,CAAC;AAEtB,wBAAgB,+BAA+B,CAC7C,CAAC,SAAS,mBAAmB,GAAG,mBAAmB,EACnD,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0FpB"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { arrowTableToJson } from '@sqlrooms/duckdb';
|
|
2
|
+
import { tool } from 'ai';
|
|
3
|
+
import { parse as vegaParse, View } from 'vega';
|
|
4
|
+
import { compile } from 'vega-lite';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { darkTheme } from './themes/darkTheme';
|
|
7
|
+
import { lightTheme } from './themes/lightTheme';
|
|
8
|
+
const TAILWIND_BACKGROUND_VARIABLE = '--background';
|
|
9
|
+
const FALLBACK_LIGHT_BACKGROUND = '#ffffff';
|
|
10
|
+
const FALLBACK_DARK_BACKGROUND = 'oklch(0.145 0 0)';
|
|
11
|
+
export const ChartImageForMarkdownToolParameters = z
|
|
12
|
+
.object({
|
|
13
|
+
documentArtifactId: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe('Target Markdown document artifact ID.'),
|
|
16
|
+
sourceToolCallId: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Prior successful chart tool call ID to render.'),
|
|
20
|
+
sqlQuery: z
|
|
21
|
+
.string()
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('SQL query to render when no sourceToolCallId is provided.'),
|
|
24
|
+
vegaLiteSpec: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Vega-Lite JSON spec string without inline data.'),
|
|
28
|
+
format: z.enum(['svg', 'png']).optional().default('svg'),
|
|
29
|
+
assetId: z.string().optional().describe('Stable document-local asset ID.'),
|
|
30
|
+
alt: z.string().describe('Markdown image alt text.'),
|
|
31
|
+
title: z.string().optional().describe('Optional image title.'),
|
|
32
|
+
width: z.number().positive().optional().default(960),
|
|
33
|
+
height: z.number().positive().optional().default(540),
|
|
34
|
+
pngScale: z.number().positive().optional().default(2),
|
|
35
|
+
renderTheme: z
|
|
36
|
+
.enum(['light', 'dark'])
|
|
37
|
+
.optional()
|
|
38
|
+
.default('light')
|
|
39
|
+
.describe('Static chart theme for the rendered image. Defaults to light for portable Markdown documents.'),
|
|
40
|
+
background: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('CSS color used as the exported image background. Defaults to the resolved Tailwind --background token for the requested theme when available.'),
|
|
44
|
+
})
|
|
45
|
+
.refine((input) => Boolean(input.sourceToolCallId) ||
|
|
46
|
+
(Boolean(input.sqlQuery) && Boolean(input.vegaLiteSpec)), {
|
|
47
|
+
message: 'Provide sourceToolCallId or both sqlQuery and vegaLiteSpec.',
|
|
48
|
+
});
|
|
49
|
+
export function createChartImageForMarkdownTool(store) {
|
|
50
|
+
return tool({
|
|
51
|
+
description: `Render an existing Vega chart analysis as a static SVG or PNG image asset for a Markdown document.
|
|
52
|
+
Use this before embedding chart images in document artifacts. The tool stores the image in the target document and returns a Markdown image link using asset://.
|
|
53
|
+
For portable Markdown documents, the default renderTheme is light with a white background. Only request dark rendering when the user explicitly wants a dark static image.`,
|
|
54
|
+
inputSchema: ChartImageForMarkdownToolParameters,
|
|
55
|
+
execute: async (params, options) => {
|
|
56
|
+
const abortSignal = options?.abortSignal;
|
|
57
|
+
try {
|
|
58
|
+
const source = resolveChartSource(store.getState(), params);
|
|
59
|
+
if (!source.success) {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
details: source.error,
|
|
63
|
+
documentArtifactId: params.documentArtifactId,
|
|
64
|
+
error: source.error,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const connector = await store.getState().db.getConnector();
|
|
68
|
+
const queryResult = await connector.query(source.sqlQuery, {
|
|
69
|
+
signal: abortSignal,
|
|
70
|
+
});
|
|
71
|
+
if (abortSignal?.aborted) {
|
|
72
|
+
throw new Error('Chart image rendering was aborted');
|
|
73
|
+
}
|
|
74
|
+
const values = arrowTableToJson(queryResult);
|
|
75
|
+
const image = await renderVegaLiteImage({
|
|
76
|
+
spec: source.vegaLiteSpec,
|
|
77
|
+
values,
|
|
78
|
+
format: params.format,
|
|
79
|
+
width: params.width,
|
|
80
|
+
height: params.height,
|
|
81
|
+
pngScale: params.pngScale,
|
|
82
|
+
renderTheme: params.renderTheme,
|
|
83
|
+
background: resolveRenderBackground(params),
|
|
84
|
+
});
|
|
85
|
+
const timestamp = Date.now();
|
|
86
|
+
const assetId = params.assetId ?? createChartAssetId(params, source.sourceToolCallId);
|
|
87
|
+
const filename = `${assetId}.${params.format}`;
|
|
88
|
+
store.getState().documents.upsertAsset(params.documentArtifactId, {
|
|
89
|
+
id: assetId,
|
|
90
|
+
mediaType: image.mediaType,
|
|
91
|
+
encoding: image.encoding,
|
|
92
|
+
data: image.data,
|
|
93
|
+
filename,
|
|
94
|
+
alt: params.alt,
|
|
95
|
+
title: params.title,
|
|
96
|
+
provenance: {
|
|
97
|
+
sourceToolCallId: source.sourceToolCallId,
|
|
98
|
+
sqlQuery: source.sqlQuery,
|
|
99
|
+
vegaLiteSpec: source.vegaLiteSpec,
|
|
100
|
+
renderTheme: params.renderTheme,
|
|
101
|
+
background: image.background,
|
|
102
|
+
renderedAt: new Date(timestamp).toISOString(),
|
|
103
|
+
},
|
|
104
|
+
createdAt: timestamp,
|
|
105
|
+
updatedAt: timestamp,
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
success: true,
|
|
109
|
+
details: `Stored ${params.format.toUpperCase()} chart image asset "${assetId}" in document "${params.documentArtifactId}".`,
|
|
110
|
+
documentArtifactId: params.documentArtifactId,
|
|
111
|
+
assetId,
|
|
112
|
+
markdown: `})`,
|
|
113
|
+
mediaType: image.mediaType,
|
|
114
|
+
encoding: image.encoding,
|
|
115
|
+
format: params.format,
|
|
116
|
+
renderTheme: params.renderTheme,
|
|
117
|
+
background: image.background,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
details: `Failed to render chart image: ${message}`,
|
|
125
|
+
documentArtifactId: params.documentArtifactId,
|
|
126
|
+
error: message,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
toModelOutput: ({ output }) => ({
|
|
131
|
+
type: 'text',
|
|
132
|
+
value: JSON.stringify(output),
|
|
133
|
+
}),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function resolveChartSource(state, params) {
|
|
137
|
+
if (params.sourceToolCallId) {
|
|
138
|
+
const output = findChartToolOutput(state, params.sourceToolCallId);
|
|
139
|
+
if (!output) {
|
|
140
|
+
return {
|
|
141
|
+
success: false,
|
|
142
|
+
error: `No successful chart tool call found for "${params.sourceToolCallId}".`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if (!output.sqlQuery || !output.vegaLiteSpec) {
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
error: `Chart tool call "${params.sourceToolCallId}" has no renderable SQL query or Vega-Lite spec.`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
sqlQuery: output.sqlQuery,
|
|
154
|
+
vegaLiteSpec: output.vegaLiteSpec,
|
|
155
|
+
sourceToolCallId: params.sourceToolCallId,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (!params.sqlQuery || !params.vegaLiteSpec) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
error: 'Provide sourceToolCallId or both sqlQuery and vegaLiteSpec.',
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
return {
|
|
166
|
+
success: true,
|
|
167
|
+
sqlQuery: params.sqlQuery,
|
|
168
|
+
vegaLiteSpec: JSON.parse(params.vegaLiteSpec),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return {
|
|
173
|
+
success: false,
|
|
174
|
+
error: `Invalid Vega-Lite JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function findChartToolOutput(state, sourceToolCallId) {
|
|
179
|
+
const currentSession = state.ai.getCurrentSession();
|
|
180
|
+
const messages = currentSession?.uiMessages ?? [];
|
|
181
|
+
for (const message of messages) {
|
|
182
|
+
if (message.role !== 'assistant')
|
|
183
|
+
continue;
|
|
184
|
+
for (const part of message.parts) {
|
|
185
|
+
if (!('toolCallId' in part) || part.toolCallId !== sourceToolCallId) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (part.state === 'output-available' &&
|
|
189
|
+
'output' in part &&
|
|
190
|
+
isVegaChartToolOutput(part.output)) {
|
|
191
|
+
return part.output;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
function isVegaChartToolOutput(value) {
|
|
198
|
+
return (typeof value === 'object' &&
|
|
199
|
+
value !== null &&
|
|
200
|
+
'success' in value &&
|
|
201
|
+
value.success === true &&
|
|
202
|
+
'sqlQuery' in value &&
|
|
203
|
+
'vegaLiteSpec' in value);
|
|
204
|
+
}
|
|
205
|
+
async function renderVegaLiteImage({ spec, values, format, width, height, pngScale, renderTheme, background, }) {
|
|
206
|
+
const themeConfig = renderTheme === 'dark' ? darkTheme : lightTheme;
|
|
207
|
+
const specWithData = {
|
|
208
|
+
padding: 10,
|
|
209
|
+
...spec,
|
|
210
|
+
background,
|
|
211
|
+
config: {
|
|
212
|
+
...themeConfig,
|
|
213
|
+
...(spec.config ?? {}),
|
|
214
|
+
background,
|
|
215
|
+
},
|
|
216
|
+
data: { values },
|
|
217
|
+
width,
|
|
218
|
+
height,
|
|
219
|
+
autosize: { contains: 'padding' },
|
|
220
|
+
};
|
|
221
|
+
const compiled = compile(specWithData).spec;
|
|
222
|
+
const view = new View(vegaParse(compiled), {
|
|
223
|
+
renderer: format === 'png' ? 'canvas' : 'none',
|
|
224
|
+
});
|
|
225
|
+
try {
|
|
226
|
+
await view.runAsync();
|
|
227
|
+
if (format === 'svg') {
|
|
228
|
+
return {
|
|
229
|
+
mediaType: 'image/svg+xml',
|
|
230
|
+
encoding: 'utf8',
|
|
231
|
+
background,
|
|
232
|
+
data: await view.toSVG(),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const canvas = await view.toCanvas(pngScale);
|
|
236
|
+
return {
|
|
237
|
+
mediaType: 'image/png',
|
|
238
|
+
encoding: 'base64',
|
|
239
|
+
background,
|
|
240
|
+
data: await canvasToBase64(canvas),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
view.finalize();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function canvasToBase64(canvas) {
|
|
248
|
+
const dataUrl = canvas.toDataURL('image/png');
|
|
249
|
+
return dataUrl.replace(/^data:image\/png;base64,/, '');
|
|
250
|
+
}
|
|
251
|
+
function createChartAssetId(params, sourceToolCallId) {
|
|
252
|
+
const base = params.title || params.alt || sourceToolCallId || 'chart';
|
|
253
|
+
const slug = base
|
|
254
|
+
.toLowerCase()
|
|
255
|
+
.trim()
|
|
256
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
257
|
+
.replace(/^-+|-+$/g, '')
|
|
258
|
+
.slice(0, 48);
|
|
259
|
+
return `${slug || 'chart'}-${Date.now().toString(36)}`;
|
|
260
|
+
}
|
|
261
|
+
function escapeMarkdownAlt(value) {
|
|
262
|
+
return value.replace(/]/g, '\\]');
|
|
263
|
+
}
|
|
264
|
+
function resolveRenderBackground(params) {
|
|
265
|
+
if (params.background)
|
|
266
|
+
return params.background;
|
|
267
|
+
const tailwindBackground = resolveTailwindBackgroundForTheme(params.renderTheme);
|
|
268
|
+
if (tailwindBackground)
|
|
269
|
+
return tailwindBackground;
|
|
270
|
+
return params.renderTheme === 'dark'
|
|
271
|
+
? FALLBACK_DARK_BACKGROUND
|
|
272
|
+
: FALLBACK_LIGHT_BACKGROUND;
|
|
273
|
+
}
|
|
274
|
+
function resolveTailwindBackgroundForTheme(renderTheme) {
|
|
275
|
+
if (typeof document === 'undefined')
|
|
276
|
+
return undefined;
|
|
277
|
+
const currentTheme = document.documentElement.classList.contains('dark')
|
|
278
|
+
? 'dark'
|
|
279
|
+
: 'light';
|
|
280
|
+
if (currentTheme !== renderTheme)
|
|
281
|
+
return undefined;
|
|
282
|
+
const value = getComputedStyle(document.documentElement)
|
|
283
|
+
.getPropertyValue(TAILWIND_BACKGROUND_VARIABLE)
|
|
284
|
+
.trim();
|
|
285
|
+
return value || undefined;
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=ChartImageForMarkdownTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartImageForMarkdownTool.js","sourceRoot":"","sources":["../src/ChartImageForMarkdownTool.ts"],"names":[],"mappings":"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","sourcesContent":["import type {AiSliceState} from '@sqlrooms/ai-core';\nimport type {DocumentsSliceState} from '@sqlrooms/documents';\nimport {arrowTableToJson, type DuckDbSliceState} from '@sqlrooms/duckdb';\nimport {tool} from 'ai';\nimport {parse as vegaParse, View} from 'vega';\nimport {compile, type TopLevelSpec} from 'vega-lite';\nimport {z} from 'zod';\nimport {darkTheme} from './themes/darkTheme';\nimport {lightTheme} from './themes/lightTheme';\nimport type {VegaChartToolOutput} from './VegaChartTool';\n\nconst TAILWIND_BACKGROUND_VARIABLE = '--background';\nconst FALLBACK_LIGHT_BACKGROUND = '#ffffff';\nconst FALLBACK_DARK_BACKGROUND = 'oklch(0.145 0 0)';\n\nexport const ChartImageForMarkdownToolParameters = z\n .object({\n documentArtifactId: z\n .string()\n .describe('Target Markdown document artifact ID.'),\n sourceToolCallId: z\n .string()\n .optional()\n .describe('Prior successful chart tool call ID to render.'),\n sqlQuery: z\n .string()\n .optional()\n .describe('SQL query to render when no sourceToolCallId is provided.'),\n vegaLiteSpec: z\n .string()\n .optional()\n .describe('Vega-Lite JSON spec string without inline data.'),\n format: z.enum(['svg', 'png']).optional().default('svg'),\n assetId: z.string().optional().describe('Stable document-local asset ID.'),\n alt: z.string().describe('Markdown image alt text.'),\n title: z.string().optional().describe('Optional image title.'),\n width: z.number().positive().optional().default(960),\n height: z.number().positive().optional().default(540),\n pngScale: z.number().positive().optional().default(2),\n renderTheme: z\n .enum(['light', 'dark'])\n .optional()\n .default('light')\n .describe(\n 'Static chart theme for the rendered image. Defaults to light for portable Markdown documents.',\n ),\n background: z\n .string()\n .optional()\n .describe(\n 'CSS color used as the exported image background. Defaults to the resolved Tailwind --background token for the requested theme when available.',\n ),\n })\n .refine(\n (input) =>\n Boolean(input.sourceToolCallId) ||\n (Boolean(input.sqlQuery) && Boolean(input.vegaLiteSpec)),\n {\n message: 'Provide sourceToolCallId or both sqlQuery and vegaLiteSpec.',\n },\n );\n\nexport type ChartImageForMarkdownToolParameters = z.infer<\n typeof ChartImageForMarkdownToolParameters\n>;\n\nexport type ChartImageForMarkdownToolOutput = {\n success: boolean;\n details: string;\n documentArtifactId: string;\n assetId?: string;\n markdown?: string;\n mediaType?: 'image/svg+xml' | 'image/png';\n encoding?: 'utf8' | 'base64';\n format?: 'svg' | 'png';\n renderTheme?: 'light' | 'dark';\n background?: string;\n error?: string;\n};\n\ntype StoreLike<S> = {\n getState: () => S;\n};\n\ntype ChartImageToolState = AiSliceState &\n DuckDbSliceState &\n DocumentsSliceState;\n\nexport function createChartImageForMarkdownTool<\n S extends ChartImageToolState = ChartImageToolState,\n>(store: StoreLike<S>) {\n return tool({\n description: `Render an existing Vega chart analysis as a static SVG or PNG image asset for a Markdown document.\nUse this before embedding chart images in document artifacts. The tool stores the image in the target document and returns a Markdown image link using asset://.\nFor portable Markdown documents, the default renderTheme is light with a white background. Only request dark rendering when the user explicitly wants a dark static image.`,\n inputSchema: ChartImageForMarkdownToolParameters,\n execute: async (params, options) => {\n const abortSignal = options?.abortSignal;\n try {\n const source = resolveChartSource(store.getState(), params);\n if (!source.success) {\n return {\n success: false,\n details: source.error,\n documentArtifactId: params.documentArtifactId,\n error: source.error,\n } satisfies ChartImageForMarkdownToolOutput;\n }\n\n const connector = await store.getState().db.getConnector();\n const queryResult = await connector.query(source.sqlQuery, {\n signal: abortSignal,\n });\n if (abortSignal?.aborted) {\n throw new Error('Chart image rendering was aborted');\n }\n\n const values = arrowTableToJson(queryResult);\n const image = await renderVegaLiteImage({\n spec: source.vegaLiteSpec,\n values,\n format: params.format,\n width: params.width,\n height: params.height,\n pngScale: params.pngScale,\n renderTheme: params.renderTheme,\n background: resolveRenderBackground(params),\n });\n const timestamp = Date.now();\n const assetId =\n params.assetId ?? createChartAssetId(params, source.sourceToolCallId);\n const filename = `${assetId}.${params.format}`;\n\n store.getState().documents.upsertAsset(params.documentArtifactId, {\n id: assetId,\n mediaType: image.mediaType,\n encoding: image.encoding,\n data: image.data,\n filename,\n alt: params.alt,\n title: params.title,\n provenance: {\n sourceToolCallId: source.sourceToolCallId,\n sqlQuery: source.sqlQuery,\n vegaLiteSpec: source.vegaLiteSpec,\n renderTheme: params.renderTheme,\n background: image.background,\n renderedAt: new Date(timestamp).toISOString(),\n },\n createdAt: timestamp,\n updatedAt: timestamp,\n });\n\n return {\n success: true,\n details: `Stored ${params.format.toUpperCase()} chart image asset \"${assetId}\" in document \"${params.documentArtifactId}\".`,\n documentArtifactId: params.documentArtifactId,\n assetId,\n markdown: `})`,\n mediaType: image.mediaType,\n encoding: image.encoding,\n format: params.format,\n renderTheme: params.renderTheme,\n background: image.background,\n } satisfies ChartImageForMarkdownToolOutput;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n details: `Failed to render chart image: ${message}`,\n documentArtifactId: params.documentArtifactId,\n error: message,\n } satisfies ChartImageForMarkdownToolOutput;\n }\n },\n toModelOutput: ({output}) => ({\n type: 'text',\n value: JSON.stringify(output),\n }),\n });\n}\n\nfunction resolveChartSource(\n state: ChartImageToolState,\n params: ChartImageForMarkdownToolParameters,\n):\n | {\n success: true;\n sqlQuery: string;\n vegaLiteSpec: TopLevelSpec;\n sourceToolCallId?: string;\n }\n | {success: false; error: string} {\n if (params.sourceToolCallId) {\n const output = findChartToolOutput(state, params.sourceToolCallId);\n if (!output) {\n return {\n success: false,\n error: `No successful chart tool call found for \"${params.sourceToolCallId}\".`,\n };\n }\n if (!output.sqlQuery || !output.vegaLiteSpec) {\n return {\n success: false,\n error: `Chart tool call \"${params.sourceToolCallId}\" has no renderable SQL query or Vega-Lite spec.`,\n };\n }\n return {\n success: true,\n sqlQuery: output.sqlQuery,\n vegaLiteSpec: output.vegaLiteSpec,\n sourceToolCallId: params.sourceToolCallId,\n };\n }\n\n if (!params.sqlQuery || !params.vegaLiteSpec) {\n return {\n success: false,\n error: 'Provide sourceToolCallId or both sqlQuery and vegaLiteSpec.',\n };\n }\n\n try {\n return {\n success: true,\n sqlQuery: params.sqlQuery,\n vegaLiteSpec: JSON.parse(params.vegaLiteSpec) as TopLevelSpec,\n };\n } catch (error) {\n return {\n success: false,\n error: `Invalid Vega-Lite JSON: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\nfunction findChartToolOutput(\n state: ChartImageToolState,\n sourceToolCallId: string,\n): VegaChartToolOutput | undefined {\n const currentSession = state.ai.getCurrentSession();\n const messages = currentSession?.uiMessages ?? [];\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const part of message.parts) {\n if (!('toolCallId' in part) || part.toolCallId !== sourceToolCallId) {\n continue;\n }\n if (\n part.state === 'output-available' &&\n 'output' in part &&\n isVegaChartToolOutput(part.output)\n ) {\n return part.output;\n }\n }\n }\n return undefined;\n}\n\nfunction isVegaChartToolOutput(value: unknown): value is VegaChartToolOutput {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'success' in value &&\n (value as VegaChartToolOutput).success === true &&\n 'sqlQuery' in value &&\n 'vegaLiteSpec' in value\n );\n}\n\nasync function renderVegaLiteImage({\n spec,\n values,\n format,\n width,\n height,\n pngScale,\n renderTheme,\n background,\n}: {\n spec: TopLevelSpec;\n values: Record<string, unknown>[];\n format: 'svg' | 'png';\n width: number;\n height: number;\n pngScale: number;\n renderTheme: 'light' | 'dark';\n background: string;\n}) {\n const themeConfig = renderTheme === 'dark' ? darkTheme : lightTheme;\n const specWithData = {\n padding: 10,\n ...spec,\n background,\n config: {\n ...themeConfig,\n ...(spec.config ?? {}),\n background,\n },\n data: {values},\n width,\n height,\n autosize: {contains: 'padding'},\n } as TopLevelSpec;\n const compiled = compile(specWithData).spec;\n const view = new View(vegaParse(compiled), {\n renderer: format === 'png' ? 'canvas' : 'none',\n });\n\n try {\n await view.runAsync();\n if (format === 'svg') {\n return {\n mediaType: 'image/svg+xml' as const,\n encoding: 'utf8' as const,\n background,\n data: await view.toSVG(),\n };\n }\n const canvas = await view.toCanvas(pngScale);\n return {\n mediaType: 'image/png' as const,\n encoding: 'base64' as const,\n background,\n data: await canvasToBase64(canvas),\n };\n } finally {\n view.finalize();\n }\n}\n\nasync function canvasToBase64(canvas: HTMLCanvasElement) {\n const dataUrl = canvas.toDataURL('image/png');\n return dataUrl.replace(/^data:image\\/png;base64,/, '');\n}\n\nfunction createChartAssetId(\n params: ChartImageForMarkdownToolParameters,\n sourceToolCallId?: string,\n) {\n const base = params.title || params.alt || sourceToolCallId || 'chart';\n const slug = base\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48);\n return `${slug || 'chart'}-${Date.now().toString(36)}`;\n}\n\nfunction escapeMarkdownAlt(value: string) {\n return value.replace(/]/g, '\\\\]');\n}\n\nfunction resolveRenderBackground(params: ChartImageForMarkdownToolParameters) {\n if (params.background) return params.background;\n const tailwindBackground = resolveTailwindBackgroundForTheme(\n params.renderTheme,\n );\n if (tailwindBackground) return tailwindBackground;\n return params.renderTheme === 'dark'\n ? FALLBACK_DARK_BACKGROUND\n : FALLBACK_LIGHT_BACKGROUND;\n}\n\nfunction resolveTailwindBackgroundForTheme(renderTheme: 'light' | 'dark') {\n if (typeof document === 'undefined') return undefined;\n const currentTheme = document.documentElement.classList.contains('dark')\n ? 'dark'\n : 'light';\n if (currentTheme !== renderTheme) return undefined;\n\n const value = getComputedStyle(document.documentElement)\n .getPropertyValue(TAILWIND_BACKGROUND_VARIABLE)\n .trim();\n return value || undefined;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ToolRendererProps } from '@sqlrooms/ai';
|
|
1
|
+
import type { ToolRendererProps } from '@sqlrooms/ai-core';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { EmbedOptions } from 'vega-embed';
|
|
4
4
|
import { EditorMode } from './editor/types';
|
|
@@ -16,5 +16,5 @@ export type VegaChartToolResultProps = ToolRendererProps<VegaChartToolOutput, Ve
|
|
|
16
16
|
* Renders a chart tool call with visualization using Vega-Lite.
|
|
17
17
|
* Shows read-only editors for inspecting spec and SQL.
|
|
18
18
|
*/
|
|
19
|
-
export declare function VegaChartToolResult({ className, output, options, editorMode, }: VegaChartToolResultProps): ReactNode;
|
|
19
|
+
export declare function VegaChartToolResult({ className, input, output, options, editorMode, }: VegaChartToolResultProps): ReactNode;
|
|
20
20
|
//# sourceMappingURL=VegaChartToolResult.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VegaChartToolResult.d.ts","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"VegaChartToolResult.d.ts","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAChC,OAAO,EAAC,YAAY,EAAoB,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAG1C,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AAKzB,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,CACtD,mBAAmB,EACnB,uBAAuB,CACxB,GAAG;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,KAAK,EACL,MAAM,EACN,OAAO,EACP,UAAmB,GACpB,EAAE,wBAAwB,GAAG,SAAS,CAgCtC"}
|
|
@@ -9,12 +9,12 @@ import { VegaLiteArrowChart } from './VegaLiteArrowChart';
|
|
|
9
9
|
* Renders a chart tool call with visualization using Vega-Lite.
|
|
10
10
|
* Shows read-only editors for inspecting spec and SQL.
|
|
11
11
|
*/
|
|
12
|
-
export function VegaChartToolResult({ className, output, options, editorMode = 'both', }) {
|
|
12
|
+
export function VegaChartToolResult({ className, input, output, options, editorMode = 'both', }) {
|
|
13
13
|
const sqlQuery = output?.sqlQuery ?? '';
|
|
14
14
|
const vegaLiteSpec = output?.vegaLiteSpec;
|
|
15
15
|
if (!vegaLiteSpec) {
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
18
|
-
return (
|
|
18
|
+
return (_jsxs("div", { className: cn('flex max-w-full flex-col gap-2', className), children: [input?.reasoning && (_jsx("p", { className: "text-tiny text-muted-foreground ml-4", children: input.reasoning })), _jsx(VegaChartContainer, { spec: vegaLiteSpec, sqlQuery: sqlQuery, options: options, editable: false, children: _jsx("div", { className: "relative max-w-full overflow-x-auto", children: _jsx(VegaChartDisplay, { aspectRatio: 16 / 9, className: "pt-2", children: _jsxs(VegaLiteArrowChart.Actions, { className: "right-3", children: [_jsx(VegaExportAction, {}), _jsx(VegaEditAction, { editorMode: editorMode })] }) }) }) })] }));
|
|
19
19
|
}
|
|
20
20
|
//# sourceMappingURL=VegaChartToolResult.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VegaChartToolResult.js","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"VegaChartToolResult.js","sourceRoot":"","sources":["../src/VegaChartToolResult.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAIhC,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAK3D,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AAexD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,SAAS,EACT,KAAK,EACL,MAAM,EACN,OAAO,EACP,UAAU,GAAG,MAAM,GACM;IACzB,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,EAAE,CAAC;IACxC,MAAM,YAAY,GAAG,MAAM,EAAE,YAAwC,CAAC;IAEtE,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,gCAAgC,EAAE,SAAS,CAAC,aAC5D,KAAK,EAAE,SAAS,IAAI,CACnB,YAAG,SAAS,EAAC,sCAAsC,YAChD,KAAK,CAAC,SAAS,GACd,CACL,EACD,KAAC,kBAAkB,IACjB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,KAAK,YAEf,cAAK,SAAS,EAAC,qCAAqC,YAClD,KAAC,gBAAgB,IAAC,WAAW,EAAE,EAAE,GAAG,CAAC,EAAE,SAAS,EAAC,MAAM,YACrD,MAAC,kBAAkB,CAAC,OAAO,IAAC,SAAS,EAAC,SAAS,aAC7C,KAAC,gBAAgB,KAAG,EACpB,KAAC,cAAc,IAAC,UAAU,EAAE,UAAU,GAAI,IACf,GACZ,GACf,GACa,IACjB,CACP,CAAC;AACJ,CAAC","sourcesContent":["import type {ToolRendererProps} from '@sqlrooms/ai-core';\nimport {cn} from '@sqlrooms/ui';\nimport {ReactNode} from 'react';\nimport {EmbedOptions, VisualizationSpec} from 'vega-embed';\nimport {EditorMode} from './editor/types';\nimport {VegaChartContainer} from './editor/VegaChartContainer';\nimport {VegaChartDisplay} from './editor/VegaChartDisplay';\nimport type {\n VegaChartToolOutput,\n VegaChartToolParameters,\n} from './VegaChartTool';\nimport {VegaEditAction} from './VegaEditAction';\nimport {VegaExportAction} from './VegaExportAction';\nimport {VegaLiteArrowChart} from './VegaLiteArrowChart';\n\nexport type VegaChartToolResultProps = ToolRendererProps<\n VegaChartToolOutput,\n VegaChartToolParameters\n> & {\n className?: string;\n options?: EmbedOptions;\n /**\n * Which editors to show when viewing\n * @default 'both'\n */\n editorMode?: EditorMode;\n};\n\n/**\n * Renders a chart tool call with visualization using Vega-Lite.\n * Shows read-only editors for inspecting spec and SQL.\n */\nexport function VegaChartToolResult({\n className,\n input,\n output,\n options,\n editorMode = 'both',\n}: VegaChartToolResultProps): ReactNode {\n const sqlQuery = output?.sqlQuery ?? '';\n const vegaLiteSpec = output?.vegaLiteSpec as VisualizationSpec | null;\n\n if (!vegaLiteSpec) {\n return null;\n }\n\n return (\n <div className={cn('flex max-w-full flex-col gap-2', className)}>\n {input?.reasoning && (\n <p className=\"text-tiny text-muted-foreground ml-4\">\n {input.reasoning}\n </p>\n )}\n <VegaChartContainer\n spec={vegaLiteSpec}\n sqlQuery={sqlQuery}\n options={options}\n editable={false}\n >\n <div className=\"relative max-w-full overflow-x-auto\">\n <VegaChartDisplay aspectRatio={16 / 9} className=\"pt-2\">\n <VegaLiteArrowChart.Actions className=\"right-3\">\n <VegaExportAction />\n <VegaEditAction editorMode={editorMode} />\n </VegaLiteArrowChart.Actions>\n </VegaChartDisplay>\n </div>\n </VegaChartContainer>\n </div>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VegaLiteArrowChart.d.ts","sourceRoot":"","sources":["../src/VegaLiteArrowChart.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,EAAC,YAAY,EAAE,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAS3D,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACjC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,UAAU,EAAE,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;IACpC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,YAAY,GACrB,YAAY,CAed;
|
|
1
|
+
{"version":3,"file":"VegaLiteArrowChart.d.ts","sourceRoot":"","sources":["../src/VegaLiteArrowChart.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,EAAC,YAAY,EAAE,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAS3D,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACjC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,UAAU,EAAE,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;IACpC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,YAAY,GACrB,YAAY,CAed;AA0HD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,kBAAkB;IAC7B;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;CAEH,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ToolErrorMessage } from '@sqlrooms/ai';
|
|
2
|
+
import { ToolErrorMessage } from '@sqlrooms/ai-core';
|
|
3
3
|
import { arrowTableToJson } from '@sqlrooms/duckdb';
|
|
4
4
|
import { AspectRatio, cn, useAspectRatioDimensions, useTheme, } from '@sqlrooms/ui';
|
|
5
5
|
import { safeJsonParse } from '@sqlrooms/utils';
|
|
@@ -48,7 +48,6 @@ const VegaLiteArrowChartBase = ({ className, aspectRatio = 16 / 9, spec, arrowTa
|
|
|
48
48
|
const specWithData = useMemo(() => {
|
|
49
49
|
const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;
|
|
50
50
|
if (!parsed) {
|
|
51
|
-
setChartError(new Error('Invalid Vega-Lite specification'));
|
|
52
51
|
return null;
|
|
53
52
|
}
|
|
54
53
|
return {
|
|
@@ -62,8 +61,13 @@ const VegaLiteArrowChartBase = ({ className, aspectRatio = 16 / 9, spec, arrowTa
|
|
|
62
61
|
autosize: { contains: 'padding' },
|
|
63
62
|
};
|
|
64
63
|
}, [spec, data]);
|
|
64
|
+
const specError = specWithData
|
|
65
|
+
? null
|
|
66
|
+
: new Error('Invalid Vega-Lite specification');
|
|
67
|
+
const displayError = specError ?? chartError;
|
|
65
68
|
// Reset chart error whenever spec or data changes
|
|
66
69
|
useEffect(() => {
|
|
70
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
67
71
|
setChartError(null);
|
|
68
72
|
}, [spec, data]);
|
|
69
73
|
const ref = useRef(null);
|
|
@@ -85,7 +89,7 @@ const VegaLiteArrowChartBase = ({ className, aspectRatio = 16 / 9, spec, arrowTa
|
|
|
85
89
|
useEffect(() => {
|
|
86
90
|
changeDimensions(dimensions.width, dimensions.height);
|
|
87
91
|
}, [changeDimensions, dimensions.width, dimensions.height]);
|
|
88
|
-
return (_jsx(VegaChartContextProvider, { value: { embed }, children: _jsxs("div", { ref: containerRef, className: cn('relative flex h-full w-full flex-col gap-2', className), children: [_jsx("div", { className: "peer relative", children:
|
|
92
|
+
return (_jsx(VegaChartContextProvider, { value: { embed }, children: _jsxs("div", { ref: containerRef, className: cn('relative flex h-full w-full flex-col gap-2', className), children: [_jsx("div", { className: "peer relative", children: displayError ? (_jsx(ToolErrorMessage, { error: displayError, triggerLabel: "Chart rendering failed", title: "Chart error", align: "start", details: spec })) : (specWithData &&
|
|
89
93
|
data && (_jsx(AspectRatio, { ratio: aspectRatio, className: "overflow-visible", asChild: true, children: _jsx("div", { ref: ref, className: "[&_svg]:overflow-visible" }) }))) }), children] }) }));
|
|
90
94
|
};
|
|
91
95
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VegaLiteArrowChart.js","sourceRoot":"","sources":["../src/VegaLiteArrowChart.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ToolErrorMessage} from '@sqlrooms/ai';\nimport {arrowTableToJson} from '@sqlrooms/duckdb';\nimport {\n AspectRatio,\n cn,\n useAspectRatioDimensions,\n useTheme,\n} from '@sqlrooms/ui';\nimport {safeJsonParse} from '@sqlrooms/utils';\nimport * as arrow from 'apache-arrow';\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport {useVegaEmbed} from 'react-vega';\nimport {EmbedOptions, VisualizationSpec} from 'vega-embed';\nimport {Config} from 'vega-lite';\nimport {darkTheme} from './themes/darkTheme';\nimport {lightTheme} from './themes/lightTheme';\nimport {VegaChartActions} from './VegaChartActions';\nimport {VegaChartContextProvider} from './VegaChartContext';\nimport {VegaEditAction} from './VegaEditAction';\nimport {VegaExportAction} from './VegaExportAction';\n\nexport type VegaLiteArrowChartProps = {\n className?: string;\n width?: number | 'auto';\n height?: number | 'auto';\n aspectRatio?: number;\n spec: string | VisualizationSpec;\n options?: EmbedOptions;\n arrowTable: arrow.Table | undefined;\n /**\n * Children for composing actions and other elements.\n * Use VegaLiteArrowChart.Actions to add action buttons.\n *\n * @example\n * ```tsx\n * <VegaLiteArrowChart spec={spec} arrowTable={data}>\n * <VegaLiteArrowChart.Actions>\n * <VegaExportAction />\n * </VegaLiteArrowChart.Actions>\n * </VegaLiteArrowChart>\n * ```\n */\n children?: React.ReactNode;\n};\n\nexport function makeDefaultVegaLiteOptions(\n options?: EmbedOptions,\n): EmbedOptions {\n return {\n mode: 'vega-lite',\n renderer: 'svg',\n theme: undefined,\n tooltip: true,\n actions: false,\n padding: {\n top: 20,\n right: 10,\n bottom: 10,\n left: 10,\n },\n ...options,\n };\n}\n\nconst VegaLiteArrowChartBase: React.FC<VegaLiteArrowChartProps> = ({\n className,\n aspectRatio = 16 / 9,\n spec,\n arrowTable,\n options: propsOptions,\n width = 'auto',\n height = 'auto',\n children,\n}) => {\n const {theme} = useTheme();\n\n const options = useMemo(\n () =>\n makeDefaultVegaLiteOptions({\n config: {\n ...(theme === 'dark' ? darkTheme : lightTheme),\n ...(typeof propsOptions?.config === 'object'\n ? (propsOptions.config as Config)\n : {}),\n },\n\n ...propsOptions,\n }),\n [theme, propsOptions],\n );\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [chartError, setChartError] = useState<Error | null>(null);\n\n const data = useMemo(() => {\n if (!arrowTable) return null;\n return {values: arrowTableToJson(arrowTable)};\n }, [arrowTable]);\n\n const specWithData = useMemo(() => {\n const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;\n if (!parsed) {\n setChartError(new Error('Invalid Vega-Lite specification'));\n return null;\n }\n return {\n padding: 10,\n background: 'transparent',\n ...parsed,\n data: data,\n // Override the following props to ensure the chart is responsive\n width: 'container',\n height: 'container',\n autosize: {contains: 'padding'},\n } as VisualizationSpec;\n }, [spec, data]);\n\n // Reset chart error whenever spec or data changes\n useEffect(() => {\n setChartError(null);\n }, [spec, data]);\n\n const ref = useRef<HTMLDivElement>(null);\n const embed = useVegaEmbed({\n ref,\n spec: specWithData ?? '',\n onError: () => setChartError,\n options,\n });\n\n const dimensions = useAspectRatioDimensions({\n containerRef,\n width,\n height,\n aspectRatio,\n });\n const changeDimensions = useCallback(\n (width: number, height: number) => {\n embed?.view.width(width).height(height).runAsync();\n },\n [embed],\n );\n useEffect(() => {\n changeDimensions(dimensions.width, dimensions.height);\n }, [changeDimensions, dimensions.width, dimensions.height]);\n\n return (\n <VegaChartContextProvider value={{embed}}>\n <div\n ref={containerRef}\n className={cn('relative flex h-full w-full flex-col gap-2', className)}\n >\n <div className=\"peer relative\">\n {chartError ? (\n <ToolErrorMessage\n error={chartError}\n triggerLabel=\"Chart rendering failed\"\n title=\"Chart error\"\n align=\"start\"\n details={spec}\n />\n ) : (\n specWithData &&\n data && (\n <AspectRatio\n ratio={aspectRatio}\n className=\"overflow-visible\"\n asChild\n >\n <div ref={ref} className=\"[&_svg]:overflow-visible\" />\n </AspectRatio>\n )\n )}\n </div>\n {children}\n </div>\n </VegaChartContextProvider>\n );\n};\n\n/**\n * Composable Vega-Lite chart component with support for custom actions.\n *\n * @example\n * ```tsx\n * // Basic usage without actions (backwards compatible)\n * <VegaLiteArrowChart spec={spec} arrowTable={data} />\n *\n * // With export action\n * <VegaLiteArrowChart spec={spec} arrowTable={data}>\n * <VegaLiteArrowChart.Actions>\n * <VegaExportAction />\n * </VegaLiteArrowChart.Actions>\n * </VegaLiteArrowChart>\n *\n * // Custom actions with separator\n * <VegaLiteArrowChart spec={spec} arrowTable={data}>\n * <VegaLiteArrowChart.Actions>\n * <VegaExportAction pngScale={3} />\n * <Separator orientation=\"vertical\" className=\"h-4\" />\n * <Button size=\"xs\" variant=\"ghost\" onClick={handleRefresh}>\n * <RefreshCw className=\"h-4 w-4\" />\n * </Button>\n * </VegaLiteArrowChart.Actions>\n * </VegaLiteArrowChart>\n * ```\n */\nexport const VegaLiteArrowChart = Object.assign(VegaLiteArrowChartBase, {\n /**\n * Container for action buttons, positioned as an overlay\n */\n Actions: VegaChartActions,\n /**\n * Built-in export action with PNG/SVG download\n */\n ExportAction: VegaExportAction,\n /**\n * Built-in edit action with spec/SQL editor popover\n */\n EditAction: VegaEditAction,\n});\n"]}
|
|
1
|
+
{"version":3,"file":"VegaLiteArrowChart.js","sourceRoot":"","sources":["../src/VegaLiteArrowChart.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAClD,OAAO,EACL,WAAW,EACX,EAAE,EACF,wBAAwB,EACxB,QAAQ,GACT,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAC,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAC;AACxE,OAAO,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAGxC,OAAO,EAAC,SAAS,EAAC,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAC,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,wBAAwB,EAAC,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AA0BpD,MAAM,UAAU,0BAA0B,CACxC,OAAsB;IAEtB,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,QAAQ,EAAE,KAAK;QACf,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,KAAK;QACd,OAAO,EAAE;YACP,GAAG,EAAE,EAAE;YACP,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;YACV,IAAI,EAAE,EAAE;SACT;QACD,GAAG,OAAO;KACX,CAAC;AACJ,CAAC;AAED,MAAM,sBAAsB,GAAsC,CAAC,EACjE,SAAS,EACT,WAAW,GAAG,EAAE,GAAG,CAAC,EACpB,IAAI,EACJ,UAAU,EACV,OAAO,EAAE,YAAY,EACrB,KAAK,GAAG,MAAM,EACd,MAAM,GAAG,MAAM,EACf,QAAQ,GACT,EAAE,EAAE;IACH,MAAM,EAAC,KAAK,EAAC,GAAG,QAAQ,EAAE,CAAC;IAE3B,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CACH,0BAA0B,CAAC;QACzB,MAAM,EAAE;YACN,GAAG,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;YAC9C,GAAG,CAAC,OAAO,YAAY,EAAE,MAAM,KAAK,QAAQ;gBAC1C,CAAC,CAAE,YAAY,CAAC,MAAiB;gBACjC,CAAC,CAAC,EAAE,CAAC;SACR;QAED,GAAG,YAAY;KAChB,CAAC,EACJ,CAAC,KAAK,EAAE,YAAY,CAAC,CACtB,CAAC;IAEF,MAAM,YAAY,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAe,IAAI,CAAC,CAAC;IAEjE,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;QACxB,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAC7B,OAAO,EAAC,MAAM,EAAE,gBAAgB,CAAC,UAAU,CAAC,EAAC,CAAC;IAChD,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACrE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,aAAa;YACzB,GAAG,MAAM;YACT,IAAI,EAAE,IAAI;YACV,iEAAiE;YACjE,KAAK,EAAE,WAAW;YAClB,MAAM,EAAE,WAAW;YACnB,QAAQ,EAAE,EAAC,QAAQ,EAAE,SAAS,EAAC;SACX,CAAC;IACzB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACjB,MAAM,SAAS,GAAG,YAAY;QAC5B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,SAAS,IAAI,UAAU,CAAC;IAE7C,kDAAkD;IAClD,SAAS,CAAC,GAAG,EAAE;QACb,2DAA2D;QAC3D,aAAa,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAEjB,MAAM,GAAG,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,YAAY,CAAC;QACzB,GAAG;QACH,IAAI,EAAE,YAAY,IAAI,EAAE;QACxB,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa;QAC5B,OAAO;KACR,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,wBAAwB,CAAC;QAC1C,YAAY;QACZ,KAAK;QACL,MAAM;QACN,WAAW;KACZ,CAAC,CAAC;IACH,MAAM,gBAAgB,GAAG,WAAW,CAClC,CAAC,KAAa,EAAE,MAAc,EAAE,EAAE;QAChC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC,EACD,CAAC,KAAK,CAAC,CACR,CAAC;IACF,SAAS,CAAC,GAAG,EAAE;QACb,gBAAgB,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC,EAAE,CAAC,gBAAgB,EAAE,UAAU,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAE5D,OAAO,CACL,KAAC,wBAAwB,IAAC,KAAK,EAAE,EAAC,KAAK,EAAC,YACtC,eACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,EAAE,CAAC,4CAA4C,EAAE,SAAS,CAAC,aAEtE,cAAK,SAAS,EAAC,eAAe,YAC3B,YAAY,CAAC,CAAC,CAAC,CACd,KAAC,gBAAgB,IACf,KAAK,EAAE,YAAY,EACnB,YAAY,EAAC,wBAAwB,EACrC,KAAK,EAAC,aAAa,EACnB,KAAK,EAAC,OAAO,EACb,OAAO,EAAE,IAAI,GACb,CACH,CAAC,CAAC,CAAC,CACF,YAAY;wBACZ,IAAI,IAAI,CACN,KAAC,WAAW,IACV,KAAK,EAAE,WAAW,EAClB,SAAS,EAAC,kBAAkB,EAC5B,OAAO,kBAEP,cAAK,GAAG,EAAE,GAAG,EAAE,SAAS,EAAC,0BAA0B,GAAG,GAC1C,CACf,CACF,GACG,EACL,QAAQ,IACL,GACmB,CAC5B,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,CAAC,sBAAsB,EAAE;IACtE;;OAEG;IACH,OAAO,EAAE,gBAAgB;IACzB;;OAEG;IACH,YAAY,EAAE,gBAAgB;IAC9B;;OAEG;IACH,UAAU,EAAE,cAAc;CAC3B,CAAC,CAAC","sourcesContent":["import {ToolErrorMessage} from '@sqlrooms/ai-core';\nimport {arrowTableToJson} from '@sqlrooms/duckdb';\nimport {\n AspectRatio,\n cn,\n useAspectRatioDimensions,\n useTheme,\n} from '@sqlrooms/ui';\nimport {safeJsonParse} from '@sqlrooms/utils';\nimport * as arrow from 'apache-arrow';\nimport {useCallback, useEffect, useMemo, useRef, useState} from 'react';\nimport {useVegaEmbed} from 'react-vega';\nimport {EmbedOptions, VisualizationSpec} from 'vega-embed';\nimport {Config} from 'vega-lite';\nimport {darkTheme} from './themes/darkTheme';\nimport {lightTheme} from './themes/lightTheme';\nimport {VegaChartActions} from './VegaChartActions';\nimport {VegaChartContextProvider} from './VegaChartContext';\nimport {VegaEditAction} from './VegaEditAction';\nimport {VegaExportAction} from './VegaExportAction';\n\nexport type VegaLiteArrowChartProps = {\n className?: string;\n width?: number | 'auto';\n height?: number | 'auto';\n aspectRatio?: number;\n spec: string | VisualizationSpec;\n options?: EmbedOptions;\n arrowTable: arrow.Table | undefined;\n /**\n * Children for composing actions and other elements.\n * Use VegaLiteArrowChart.Actions to add action buttons.\n *\n * @example\n * ```tsx\n * <VegaLiteArrowChart spec={spec} arrowTable={data}>\n * <VegaLiteArrowChart.Actions>\n * <VegaExportAction />\n * </VegaLiteArrowChart.Actions>\n * </VegaLiteArrowChart>\n * ```\n */\n children?: React.ReactNode;\n};\n\nexport function makeDefaultVegaLiteOptions(\n options?: EmbedOptions,\n): EmbedOptions {\n return {\n mode: 'vega-lite',\n renderer: 'svg',\n theme: undefined,\n tooltip: true,\n actions: false,\n padding: {\n top: 20,\n right: 10,\n bottom: 10,\n left: 10,\n },\n ...options,\n };\n}\n\nconst VegaLiteArrowChartBase: React.FC<VegaLiteArrowChartProps> = ({\n className,\n aspectRatio = 16 / 9,\n spec,\n arrowTable,\n options: propsOptions,\n width = 'auto',\n height = 'auto',\n children,\n}) => {\n const {theme} = useTheme();\n\n const options = useMemo(\n () =>\n makeDefaultVegaLiteOptions({\n config: {\n ...(theme === 'dark' ? darkTheme : lightTheme),\n ...(typeof propsOptions?.config === 'object'\n ? (propsOptions.config as Config)\n : {}),\n },\n\n ...propsOptions,\n }),\n [theme, propsOptions],\n );\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [chartError, setChartError] = useState<Error | null>(null);\n\n const data = useMemo(() => {\n if (!arrowTable) return null;\n return {values: arrowTableToJson(arrowTable)};\n }, [arrowTable]);\n\n const specWithData = useMemo(() => {\n const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;\n if (!parsed) {\n return null;\n }\n return {\n padding: 10,\n background: 'transparent',\n ...parsed,\n data: data,\n // Override the following props to ensure the chart is responsive\n width: 'container',\n height: 'container',\n autosize: {contains: 'padding'},\n } as VisualizationSpec;\n }, [spec, data]);\n const specError = specWithData\n ? null\n : new Error('Invalid Vega-Lite specification');\n const displayError = specError ?? chartError;\n\n // Reset chart error whenever spec or data changes\n useEffect(() => {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setChartError(null);\n }, [spec, data]);\n\n const ref = useRef<HTMLDivElement>(null);\n const embed = useVegaEmbed({\n ref,\n spec: specWithData ?? '',\n onError: () => setChartError,\n options,\n });\n\n const dimensions = useAspectRatioDimensions({\n containerRef,\n width,\n height,\n aspectRatio,\n });\n const changeDimensions = useCallback(\n (width: number, height: number) => {\n embed?.view.width(width).height(height).runAsync();\n },\n [embed],\n );\n useEffect(() => {\n changeDimensions(dimensions.width, dimensions.height);\n }, [changeDimensions, dimensions.width, dimensions.height]);\n\n return (\n <VegaChartContextProvider value={{embed}}>\n <div\n ref={containerRef}\n className={cn('relative flex h-full w-full flex-col gap-2', className)}\n >\n <div className=\"peer relative\">\n {displayError ? (\n <ToolErrorMessage\n error={displayError}\n triggerLabel=\"Chart rendering failed\"\n title=\"Chart error\"\n align=\"start\"\n details={spec}\n />\n ) : (\n specWithData &&\n data && (\n <AspectRatio\n ratio={aspectRatio}\n className=\"overflow-visible\"\n asChild\n >\n <div ref={ref} className=\"[&_svg]:overflow-visible\" />\n </AspectRatio>\n )\n )}\n </div>\n {children}\n </div>\n </VegaChartContextProvider>\n );\n};\n\n/**\n * Composable Vega-Lite chart component with support for custom actions.\n *\n * @example\n * ```tsx\n * // Basic usage without actions (backwards compatible)\n * <VegaLiteArrowChart spec={spec} arrowTable={data} />\n *\n * // With export action\n * <VegaLiteArrowChart spec={spec} arrowTable={data}>\n * <VegaLiteArrowChart.Actions>\n * <VegaExportAction />\n * </VegaLiteArrowChart.Actions>\n * </VegaLiteArrowChart>\n *\n * // Custom actions with separator\n * <VegaLiteArrowChart spec={spec} arrowTable={data}>\n * <VegaLiteArrowChart.Actions>\n * <VegaExportAction pngScale={3} />\n * <Separator orientation=\"vertical\" className=\"h-4\" />\n * <Button size=\"xs\" variant=\"ghost\" onClick={handleRefresh}>\n * <RefreshCw className=\"h-4 w-4\" />\n * </Button>\n * </VegaLiteArrowChart.Actions>\n * </VegaLiteArrowChart>\n * ```\n */\nexport const VegaLiteArrowChart = Object.assign(VegaLiteArrowChartBase, {\n /**\n * Container for action buttons, positioned as an overlay\n */\n Actions: VegaChartActions,\n /**\n * Built-in export action with PNG/SVG download\n */\n ExportAction: VegaExportAction,\n /**\n * Built-in edit action with spec/SQL editor popover\n */\n EditAction: VegaEditAction,\n});\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ToolErrorMessage } from '@sqlrooms/ai';
|
|
2
|
+
import { ToolErrorMessage } from '@sqlrooms/ai-core';
|
|
3
3
|
import { useSql } from '@sqlrooms/duckdb';
|
|
4
4
|
import { cn } from '@sqlrooms/ui';
|
|
5
5
|
import { VegaLiteArrowChart } from '../VegaLiteArrowChart';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VegaChartDisplay.js","sourceRoot":"","sources":["../../src/editor/VegaChartDisplay.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"VegaChartDisplay.js","sourceRoot":"","sources":["../../src/editor/VegaChartDisplay.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAEhC,OAAO,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AAkBzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,SAAS,EACT,WAAW,GAAG,EAAE,GAAG,CAAC,EACpB,QAAQ,GACT,EAAE,EAAE;IACH,MAAM,EAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAC,GAAG,oBAAoB,EAAE,CAAC;IAE5D,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC;IAElC,gDAAgD;IAChD,MAAM,SAAS,GAAG,MAAM,CAAC;QACvB,KAAK,EAAE,QAAQ,IAAI,EAAE;QACrB,OAAO,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,UAAU;KACnC,CAAC,CAAC;IAEH,wDAAwD;IACxD,MAAM,SAAS,GAAG,UAAU,IAAI,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC;IAE3D,iFAAiF;IACjF,8EAA8E;IAC9E,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,aAAa,CAAC;IAErD,qBAAqB;IACrB,IAAI,QAAQ,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;QACnD,OAAO,CACL,eACE,SAAS,EAAE,EAAE,CACX,wDAAwD,EACxD,SAAS,CACV,aAED,cAAK,SAAS,EAAC,8EAA8E,GAAG,EAChG,eAAM,SAAS,EAAC,cAAc,gCAAuB,IACjD,CACP,CAAC;IACJ,CAAC;IAED,mBAAmB;IACnB,IAAI,QAAQ,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;QAC/C,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,YACvB,KAAC,gBAAgB,IACf,KAAK,EAAE,SAAS,CAAC,KAAK,EACtB,YAAY,EAAC,kBAAkB,EAC/B,KAAK,EAAC,aAAa,EACnB,KAAK,EAAC,OAAO,EACb,OAAO,EAAE,QAAQ,GACjB,GACE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,cAAK,SAAS,EAAE,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,YACvC,KAAC,kBAAkB,IACjB,SAAS,EAAC,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,SAAS,EACrB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,YAEf,QAAQ,GACU,GACjB,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {ToolErrorMessage} from '@sqlrooms/ai-core';\nimport {useSql} from '@sqlrooms/duckdb';\nimport {cn} from '@sqlrooms/ui';\nimport React from 'react';\nimport {VegaLiteArrowChart} from '../VegaLiteArrowChart';\nimport {useVegaEditorContext} from './VegaEditorContext';\n\nexport interface VegaChartDisplayProps {\n /**\n * Custom class name for the chart container\n */\n className?: string;\n /**\n * Aspect ratio for the chart\n * @default 16/9\n */\n aspectRatio?: number;\n /**\n * Children passed through to VegaLiteArrowChart (e.g., action components)\n */\n children?: React.ReactNode;\n}\n\n/**\n * Chart display subcomponent for VegaLiteChart.Container.\n * Renders the Vega-Lite chart with data from SQL query or arrow table.\n *\n * Uses the parsed spec from the editor state for live preview during editing.\n *\n * Must be used within a VegaLiteChart.Container component.\n *\n * @example\n * ```tsx\n * <VegaLiteChart.Container spec={spec} sqlQuery={query}>\n * <VegaLiteChart.Chart />\n * </VegaLiteChart.Container>\n * ```\n */\nexport const VegaChartDisplay: React.FC<VegaChartDisplayProps> = ({\n className,\n aspectRatio = 16 / 9,\n children,\n}) => {\n const {state, arrowTable, options} = useVegaEditorContext();\n\n // Use the applied SQL for chart rendering (updates when Apply is clicked)\n const sqlQuery = state.appliedSql;\n\n // Fetch data from SQL if no arrowTable provided\n const sqlResult = useSql({\n query: sqlQuery || '',\n enabled: !!sqlQuery && !arrowTable,\n });\n\n // Use arrow table if provided, otherwise use SQL result\n const chartData = arrowTable ?? sqlResult.data?.arrowTable;\n\n // Use parsed spec for live preview, fall back to last valid spec if parse failed\n // This ensures the chart keeps rendering during typing even with invalid JSON\n const spec = state.parsedSpec ?? state.lastValidSpec;\n\n // Show loading state\n if (sqlQuery && !arrowTable && sqlResult.isLoading) {\n return (\n <div\n className={cn(\n 'text-muted-foreground flex items-center justify-center',\n className,\n )}\n >\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600\" />\n <span className=\"ml-2 text-sm\">Loading data...</span>\n </div>\n );\n }\n\n // Show error state\n if (sqlQuery && !arrowTable && sqlResult.error) {\n return (\n <div className={className}>\n <ToolErrorMessage\n error={sqlResult.error}\n triggerLabel=\"SQL query failed\"\n title=\"Query error\"\n align=\"start\"\n details={sqlQuery}\n />\n </div>\n );\n }\n\n return (\n <div className={cn('relative', className)}>\n <VegaLiteArrowChart\n className=\"w-full\"\n spec={spec}\n arrowTable={chartData}\n aspectRatio={aspectRatio}\n options={options}\n >\n {children}\n </VegaLiteArrowChart>\n </div>\n );\n};\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type { VisualizationSpec } from 'vega-embed';
|
|
|
7
7
|
export type { SignalListenerHandler, SignalValue } from 'vega';
|
|
8
8
|
export { createVegaChartTool, createSqlValidator, VegaChartToolParameters, DEFAULT_VEGA_CHART_DESCRIPTION, } from './VegaChartTool';
|
|
9
9
|
export type { VegaChartToolOptions, VegaChartToolOutput } from './VegaChartTool';
|
|
10
|
+
export { createChartImageForMarkdownTool, ChartImageForMarkdownToolParameters, } from './ChartImageForMarkdownTool';
|
|
11
|
+
export type { ChartImageForMarkdownToolOutput, ChartImageForMarkdownToolParameters as ChartImageForMarkdownToolParametersType, } from './ChartImageForMarkdownTool';
|
|
10
12
|
/**
|
|
11
13
|
* Composable Vega-Lite chart component with editing capabilities.
|
|
12
14
|
*
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,EAAC,mBAAmB,IAAI,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AACjF,YAAY,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAClD,YAAY,EAAC,qBAAqB,EAAE,WAAW,EAAC,MAAM,MAAM,CAAC;AAE7D,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,EAAC,mBAAmB,IAAI,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AACjF,YAAY,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAClD,YAAY,EAAC,qBAAqB,EAAE,WAAW,EAAC,MAAM,MAAM,CAAC;AAE7D,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EAAC,oBAAoB,EAAE,mBAAmB,EAAC,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EACL,+BAA+B,EAC/B,mCAAmC,GACpC,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,+BAA+B,EAC/B,mCAAmC,IAAI,uCAAuC,GAC/E,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;CAUxB,CAAC;AAGH,mDAAmD;AACnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAC,oBAAoB,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAC,oBAAoB,EAAC,MAAM,4BAA4B,CAAC;AAGhE,YAAY,EACV,UAAU,EACV,YAAY,EACZ,WAAW,EACX,yBAAyB,EACzB,wBAAwB,EACxB,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAC,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC;AAG9D,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAChD,YAAY,EAAC,mBAAmB,EAAC,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,YAAY,EAAC,qBAAqB,EAAC,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { VegaSqlEditorPanel } from './editor/VegaSqlEditorPanel';
|
|
|
11
11
|
import { VegaChartEditorActions } from './editor/VegaChartEditorActions';
|
|
12
12
|
export { VegaChartToolResult as VegaChartToolResult } from './VegaChartToolResult';
|
|
13
13
|
export { createVegaChartTool, createSqlValidator, VegaChartToolParameters, DEFAULT_VEGA_CHART_DESCRIPTION, } from './VegaChartTool';
|
|
14
|
+
export { createChartImageForMarkdownTool, ChartImageForMarkdownToolParameters, } from './ChartImageForMarkdownTool';
|
|
14
15
|
/**
|
|
15
16
|
* Composable Vega-Lite chart component with editing capabilities.
|
|
16
17
|
*
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAC,mBAAmB,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAC,sBAAsB,EAAC,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAC,mBAAmB,IAAI,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAIjF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAC,mBAAmB,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAC,sBAAsB,EAAC,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAC,mBAAmB,IAAI,mBAAmB,EAAC,MAAM,uBAAuB,CAAC;AAIjF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,+BAA+B,EAC/B,mCAAmC,GACpC,MAAM,6BAA6B,CAAC;AAMrC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE;IAC3D,0BAA0B;IAC1B,QAAQ,EAAE,gBAAgB;IAC1B,UAAU,EAAE,kBAAkB;IAC9B,6BAA6B;IAC7B,SAAS,EAAE,kBAAkB;IAC7B,KAAK,EAAE,gBAAgB;IACvB,UAAU,EAAE,mBAAmB;IAC/B,SAAS,EAAE,kBAAkB;IAC7B,OAAO,EAAE,sBAAsB;CAChC,CAAC,CAAC;AAEH,sDAAsD;AACtD,mDAAmD;AACnD,OAAO,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAC,oBAAoB,EAAC,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAC,kBAAkB,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAC,oBAAoB,EAAC,MAAM,4BAA4B,CAAC;AAchE,0BAA0B;AAC1B,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAEjC,mDAAmD;AACnD,OAAO,EAAC,mBAAmB,EAAC,MAAM,oBAAoB,CAAC;AAGvD,2BAA2B;AAC3B,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAC,cAAc,EAAC,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC","sourcesContent":["/**\n * {@include ../README.md}\n * @packageDocumentation\n */\n\nimport {VegaLiteSqlChart} from './VegaLiteSqlChart';\nimport {VegaLiteArrowChart} from './VegaLiteArrowChart';\nimport {VegaChartContainer} from './editor/VegaChartContainer';\nimport {VegaChartDisplay} from './editor/VegaChartDisplay';\nimport {VegaSpecEditorPanel} from './editor/VegaSpecEditorPanel';\nimport {VegaSqlEditorPanel} from './editor/VegaSqlEditorPanel';\nimport {VegaChartEditorActions} from './editor/VegaChartEditorActions';\nexport {VegaChartToolResult as VegaChartToolResult} from './VegaChartToolResult';\nexport type {VisualizationSpec} from 'vega-embed';\nexport type {SignalListenerHandler, SignalValue} from 'vega';\n\nexport {\n createVegaChartTool,\n createSqlValidator,\n VegaChartToolParameters,\n DEFAULT_VEGA_CHART_DESCRIPTION,\n} from './VegaChartTool';\n\nexport type {VegaChartToolOptions, VegaChartToolOutput} from './VegaChartTool';\nexport {\n createChartImageForMarkdownTool,\n ChartImageForMarkdownToolParameters,\n} from './ChartImageForMarkdownTool';\nexport type {\n ChartImageForMarkdownToolOutput,\n ChartImageForMarkdownToolParameters as ChartImageForMarkdownToolParametersType,\n} from './ChartImageForMarkdownTool';\n\n/**\n * Composable Vega-Lite chart component with editing capabilities.\n *\n * @example\n * ```tsx\n * // Compound component pattern\n * <VegaLiteChart.Container\n * spec={mySpec}\n * sqlQuery={myQuery}\n * editable={true}\n * onSpecChange={(spec) => saveSpec(spec)}\n * >\n * <VegaLiteChart.Chart />\n * <VegaLiteChart.SpecEditor />\n * <VegaLiteChart.SqlEditor />\n * <VegaLiteChart.Actions />\n * </VegaLiteChart.Container>\n *\n * // Simple usage (legacy)\n * <VegaLiteChart spec={mySpec} sqlQuery={myQuery} />\n * ```\n */\nexport const VegaLiteChart = Object.assign(VegaLiteSqlChart, {\n // Legacy chart components\n SqlChart: VegaLiteSqlChart,\n ArrowChart: VegaLiteArrowChart,\n // Compound editor components\n Container: VegaChartContainer,\n Chart: VegaChartDisplay,\n SpecEditor: VegaSpecEditorPanel,\n SqlEditor: VegaSqlEditorPanel,\n Actions: VegaChartEditorActions,\n});\n\n// Export editor components and hooks for advanced use\n/** @deprecated Use VegaCodeMirrorEditor instead */\nexport {VegaMonacoEditor} from './editor/VegaMonacoEditor';\nexport {VegaCodeMirrorEditor} from './editor/VegaCodeMirrorEditor';\nexport {useVegaChartEditor} from './editor/useVegaChartEditor';\nexport {useVegaEditorContext} from './editor/VegaEditorContext';\n\n// Export editor types\nexport type {\n EditorMode,\n OnSpecChange,\n OnSqlChange,\n UseVegaChartEditorOptions,\n UseVegaChartEditorReturn,\n VegaEditorActions,\n VegaEditorContextValue,\n VegaEditorState,\n} from './editor/types';\n\n// Export schema utilities\nexport {\n loadVegaLiteSchema,\n getCachedVegaLiteSchema,\n preloadVegaLiteSchema,\n} from './schema/vegaLiteSchema';\n\n// Export chart context and hook for custom actions\nexport {useVegaChartContext} from './VegaChartContext';\nexport type {VegaChartContextValue} from './VegaChartContext';\n\n// Export action components\nexport {VegaExportAction} from './VegaExportAction';\nexport type {VegaExportActionProps} from './VegaExportAction';\nexport {VegaEditAction} from './VegaEditAction';\nexport type {VegaEditActionProps} from './VegaEditAction';\nexport {VegaChartActions} from './VegaChartActions';\nexport type {VegaChartActionsProps} from './VegaChartActions';\n"]}
|
package/package.json
CHANGED
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/vega",
|
|
3
|
-
"version": "0.29.0-rc.
|
|
4
|
-
"main": "dist/index.js",
|
|
5
|
-
"types": "dist/index.d.ts",
|
|
6
|
-
"module": "dist/index.js",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"sideEffects": false,
|
|
9
|
-
"author": "SQLRooms Contributors",
|
|
10
|
-
"license": "MIT",
|
|
3
|
+
"version": "0.29.0-rc.4",
|
|
11
4
|
"repository": {
|
|
12
5
|
"type": "git",
|
|
13
6
|
"url": "git+https://github.com/sqlrooms/sqlrooms.git"
|
|
14
7
|
},
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"author": "SQLRooms Contributors",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"module": "dist/index.js",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "tsc -w",
|
|
21
|
+
"lint": "eslint .",
|
|
22
|
+
"typecheck": "tsc --noEmit",
|
|
23
|
+
"typedoc": "typedoc"
|
|
20
24
|
},
|
|
21
25
|
"dependencies": {
|
|
22
|
-
"@sqlrooms/ai": "0.29.0-rc.
|
|
23
|
-
"@sqlrooms/codemirror": "0.29.0-rc.
|
|
24
|
-
"@sqlrooms/
|
|
25
|
-
"@sqlrooms/
|
|
26
|
-
"@sqlrooms/
|
|
27
|
-
"@sqlrooms/
|
|
28
|
-
"@sqlrooms/
|
|
26
|
+
"@sqlrooms/ai-core": "0.29.0-rc.4",
|
|
27
|
+
"@sqlrooms/codemirror": "0.29.0-rc.4",
|
|
28
|
+
"@sqlrooms/documents": "0.29.0-rc.4",
|
|
29
|
+
"@sqlrooms/duckdb": "0.29.0-rc.4",
|
|
30
|
+
"@sqlrooms/monaco-editor": "0.29.0-rc.4",
|
|
31
|
+
"@sqlrooms/sql-editor": "0.29.0-rc.4",
|
|
32
|
+
"@sqlrooms/ui": "0.29.0-rc.4",
|
|
33
|
+
"@sqlrooms/utils": "0.29.0-rc.4",
|
|
29
34
|
"ai": "^6.0.154",
|
|
30
35
|
"lucide-react": "^0.556.0",
|
|
31
36
|
"react-vega": "^8.0.0",
|
|
@@ -42,12 +47,8 @@
|
|
|
42
47
|
"react": ">=18",
|
|
43
48
|
"react-dom": ">=18"
|
|
44
49
|
},
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"build": "tsc",
|
|
48
|
-
"lint": "eslint .",
|
|
49
|
-
"typecheck": "tsc --noEmit",
|
|
50
|
-
"typedoc": "typedoc"
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
51
52
|
},
|
|
52
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "8694bab9d3d915a06d28675ad801b21444bcc55f"
|
|
53
54
|
}
|
package/dist/VegaLiteChart.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import * as arrow from 'apache-arrow';
|
|
2
|
-
import { VisualizationSpec } from 'react-vega';
|
|
3
|
-
export declare const ArrowChart: React.FC<{
|
|
4
|
-
className?: string;
|
|
5
|
-
width?: number | 'auto';
|
|
6
|
-
height?: number | 'auto';
|
|
7
|
-
aspectRatio?: number;
|
|
8
|
-
spec: string | VisualizationSpec;
|
|
9
|
-
arrowTable: arrow.Table | undefined;
|
|
10
|
-
dataName?: string;
|
|
11
|
-
}>;
|
|
12
|
-
export declare const VegaLiteChart: import("react").FC<{
|
|
13
|
-
className?: string;
|
|
14
|
-
width?: number | "auto";
|
|
15
|
-
height?: number | "auto";
|
|
16
|
-
aspectRatio?: number;
|
|
17
|
-
sqlQuery: string;
|
|
18
|
-
spec: string | VisualizationSpec;
|
|
19
|
-
dataName?: string;
|
|
20
|
-
lastRunTime?: number;
|
|
21
|
-
isLoading?: boolean;
|
|
22
|
-
}> & {
|
|
23
|
-
SqlChart: import("react").FC<{
|
|
24
|
-
className?: string;
|
|
25
|
-
width?: number | "auto";
|
|
26
|
-
height?: number | "auto";
|
|
27
|
-
aspectRatio?: number;
|
|
28
|
-
sqlQuery: string;
|
|
29
|
-
spec: string | VisualizationSpec;
|
|
30
|
-
dataName?: string;
|
|
31
|
-
lastRunTime?: number;
|
|
32
|
-
isLoading?: boolean;
|
|
33
|
-
}>;
|
|
34
|
-
ArrowChart: import("react").FC<{
|
|
35
|
-
className?: string;
|
|
36
|
-
width?: number | "auto";
|
|
37
|
-
height?: number | "auto";
|
|
38
|
-
aspectRatio?: number;
|
|
39
|
-
spec: string | VisualizationSpec;
|
|
40
|
-
arrowTable: arrow.Table | undefined;
|
|
41
|
-
dataName?: string;
|
|
42
|
-
}>;
|
|
43
|
-
};
|
|
44
|
-
//# sourceMappingURL=VegaLiteChart.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VegaLiteChart.d.ts","sourceRoot":"","sources":["../src/VegaLiteChart.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,EAAW,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAmIvD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACjC,UAAU,EAAE,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAwEA,CAAC;AAEF,eAAO,MAAM,aAAa;gBA9JZ,MAAM;YACV,MAAM,GAAG,MAAM;aACd,MAAM,GAAG,MAAM;kBACV,MAAM;cACV,MAAM;UACV,MAAM,GAAG,iBAAiB;eACrB,MAAM;kBACH,MAAM;gBACR,OAAO;;;oBARP,MAAM;gBACV,MAAM,GAAG,MAAM;iBACd,MAAM,GAAG,MAAM;sBACV,MAAM;kBACV,MAAM;cACV,MAAM,GAAG,iBAAiB;mBACrB,MAAM;sBACH,MAAM;oBACR,OAAO;;;oBAqEP,MAAM;gBACV,MAAM,GAAG,MAAM;iBACd,MAAM,GAAG,MAAM;sBACV,MAAM;cACd,MAAM,GAAG,iBAAiB;oBACpB,KAAK,CAAC,KAAK,GAAG,SAAS;mBACxB,MAAM;;CA8EjB,CAAC"}
|
package/dist/VegaLiteChart.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ToolErrorMessage } from '@sqlrooms/ai';
|
|
3
|
-
import { arrowTableToJson, useSql } from '@sqlrooms/duckdb';
|
|
4
|
-
import { AspectRatio, cn, useAspectRatioDimensions } from '@sqlrooms/ui';
|
|
5
|
-
import { safeJsonParse } from '@sqlrooms/utils';
|
|
6
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
7
|
-
import { VegaLite } from 'react-vega';
|
|
8
|
-
const DEFAULT_DATA_NAME = 'queryResult';
|
|
9
|
-
/**
|
|
10
|
-
* A component that renders a Vega-Lite chart with SQL data and responsive sizing.
|
|
11
|
-
*
|
|
12
|
-
* The chart can be sized in multiple ways:
|
|
13
|
-
* - Fixed dimensions: Provide both width and height as numbers
|
|
14
|
-
* - Fixed width, proportional height: Provide width as number, height as 'auto'
|
|
15
|
-
* - Fixed height, proportional width: Provide height as number, width as 'auto'
|
|
16
|
-
* - Fully responsive: Leave both as 'auto' (default), chart will fill container while maintaining aspect ratio
|
|
17
|
-
*
|
|
18
|
-
* @param props - The component props
|
|
19
|
-
* @param {number | 'auto'} [props.width='auto'] - The chart width in pixels, or 'auto' to use container width
|
|
20
|
-
* @param {number | 'auto'} [props.height='auto'] - The chart height in pixels, or 'auto' to calculate from aspect ratio
|
|
21
|
-
* @param {number} [props.aspectRatio=3/2] - The desired width-to-height ratio when dimensions are auto-calculated
|
|
22
|
-
* @param {string} props.sqlQuery - The SQL query to fetch data for the chart
|
|
23
|
-
* @param {string | VisualizationSpec} props.spec - The Vega-Lite specification for the chart.
|
|
24
|
-
* Can be either a JSON string or a VisualizationSpec object.
|
|
25
|
-
* The data and size properties will be overridden by the component.
|
|
26
|
-
*
|
|
27
|
-
* @returns The rendered chart component
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* // Fixed size chart
|
|
31
|
-
* <VegaLiteChart
|
|
32
|
-
* width={600}
|
|
33
|
-
* height={400}
|
|
34
|
-
* sqlQuery="SELECT category, count(*) as count FROM sales GROUP BY category"
|
|
35
|
-
* spec={{
|
|
36
|
-
* mark: 'bar',
|
|
37
|
-
* encoding: {
|
|
38
|
-
* x: {field: 'category', type: 'nominal'},
|
|
39
|
-
* y: {field: 'count', type: 'quantitative'}
|
|
40
|
-
* }
|
|
41
|
-
* }}
|
|
42
|
-
* />
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* // Responsive chart with 16:9 aspect ratio
|
|
46
|
-
* <VegaLiteChart
|
|
47
|
-
* className="max-w-[600px]"
|
|
48
|
-
* aspectRatio={16/9}
|
|
49
|
-
* sqlQuery="SELECT date, value FROM metrics"
|
|
50
|
-
* spec={{
|
|
51
|
-
* mark: 'line',
|
|
52
|
-
* encoding: {
|
|
53
|
-
* x: {field: 'date', type: 'temporal'},
|
|
54
|
-
* y: {field: 'value', type: 'quantitative'}
|
|
55
|
-
* }
|
|
56
|
-
* }}
|
|
57
|
-
* />
|
|
58
|
-
*/
|
|
59
|
-
const VegaLiteSqlChart = ({ className, width = 'auto', height = 'auto', aspectRatio, sqlQuery, spec, dataName = DEFAULT_DATA_NAME, lastRunTime, isLoading, }) => {
|
|
60
|
-
const containerRef = useRef(null);
|
|
61
|
-
const dimensions = useAspectRatioDimensions({
|
|
62
|
-
containerRef,
|
|
63
|
-
width,
|
|
64
|
-
height,
|
|
65
|
-
aspectRatio,
|
|
66
|
-
});
|
|
67
|
-
const { width: adjustedWidth, height: adjustedHeight } = dimensions;
|
|
68
|
-
const refinedSpec = useMemo(() => {
|
|
69
|
-
const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;
|
|
70
|
-
if (!parsed)
|
|
71
|
-
return null;
|
|
72
|
-
return {
|
|
73
|
-
...parsed,
|
|
74
|
-
data: { name: dataName },
|
|
75
|
-
width: adjustedWidth,
|
|
76
|
-
height: adjustedHeight,
|
|
77
|
-
autosize: {
|
|
78
|
-
type: 'fit',
|
|
79
|
-
contains: 'padding',
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}, [spec, dataName, adjustedWidth, adjustedHeight]);
|
|
83
|
-
const result = useSql({ query: sqlQuery, version: lastRunTime });
|
|
84
|
-
const arrowTable = result.data?.arrowTable;
|
|
85
|
-
const data = useMemo(() => {
|
|
86
|
-
if (!arrowTable)
|
|
87
|
-
return null;
|
|
88
|
-
return { queryResult: arrowTableToJson(arrowTable) };
|
|
89
|
-
}, [arrowTable]);
|
|
90
|
-
return (_jsx("div", { ref: containerRef, className: cn('flex h-full w-full flex-col gap-2 overflow-hidden', className), children: isLoading || result.isLoading ? (_jsxs("div", { className: "text-muted-foreground flex items-center justify-center gap-2 p-2", children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600" }), "Running query for chart data\u2026"] })) : refinedSpec && data ? (_jsx(AspectRatio, { ratio: aspectRatio, children: _jsx(VegaLite, { spec: refinedSpec, data: data }) })) : result.error ? (_jsx("div", { className: "whitespace-pre-wrap p-2 font-mono text-sm text-red-500", children: result.error.message })) : null }));
|
|
91
|
-
};
|
|
92
|
-
export const ArrowChart = ({ className, width = 'auto', height = 'auto', aspectRatio = 3 / 2, spec, arrowTable, dataName = DEFAULT_DATA_NAME, }) => {
|
|
93
|
-
const containerRef = useRef(null);
|
|
94
|
-
const [chartError, setChartError] = useState(null);
|
|
95
|
-
const dimensions = useAspectRatioDimensions({
|
|
96
|
-
containerRef,
|
|
97
|
-
width,
|
|
98
|
-
height,
|
|
99
|
-
aspectRatio,
|
|
100
|
-
});
|
|
101
|
-
const refinedSpec = useMemo(() => {
|
|
102
|
-
const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;
|
|
103
|
-
if (!parsed) {
|
|
104
|
-
setChartError(new Error('Invalid Vega-Lite specification'));
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
return {
|
|
108
|
-
...parsed,
|
|
109
|
-
data: { name: dataName },
|
|
110
|
-
width: dimensions.width,
|
|
111
|
-
height: dimensions.height,
|
|
112
|
-
autosize: {
|
|
113
|
-
type: 'fit',
|
|
114
|
-
contains: 'padding',
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
}, [spec, dataName, dimensions]);
|
|
118
|
-
const data = useMemo(() => {
|
|
119
|
-
if (!arrowTable)
|
|
120
|
-
return null;
|
|
121
|
-
return { queryResult: arrowTableToJson(arrowTable) };
|
|
122
|
-
}, [arrowTable]);
|
|
123
|
-
// Reset chart error whenever spec or data changes
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
setChartError(null);
|
|
126
|
-
}, [spec, data]);
|
|
127
|
-
return (_jsx("div", { ref: containerRef, className: cn('flex h-full w-full flex-col gap-2 overflow-hidden', className), children: chartError ? (_jsx(ToolErrorMessage, { error: chartError, triggerLabel: "Chart rendering failed", title: "Chart error", align: "start", details: spec })) : (refinedSpec &&
|
|
128
|
-
data && (_jsx(AspectRatio, { ratio: aspectRatio, children: _jsx(VegaLite, { spec: refinedSpec, data: data, onError: setChartError }) }))) }));
|
|
129
|
-
};
|
|
130
|
-
export const VegaLiteChart = Object.assign(VegaLiteSqlChart, {
|
|
131
|
-
SqlChart: VegaLiteSqlChart,
|
|
132
|
-
ArrowChart: ArrowChart,
|
|
133
|
-
});
|
|
134
|
-
//# sourceMappingURL=VegaLiteChart.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"VegaLiteChart.js","sourceRoot":"","sources":["../src/VegaLiteChart.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ToolErrorMessage} from '@sqlrooms/ai';\nimport {arrowTableToJson, useSql} from '@sqlrooms/duckdb';\nimport {AspectRatio, cn, useAspectRatioDimensions} from '@sqlrooms/ui';\nimport {safeJsonParse} from '@sqlrooms/utils';\nimport * as arrow from 'apache-arrow';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {VegaLite, VisualizationSpec} from 'react-vega';\n\nconst DEFAULT_DATA_NAME = 'queryResult';\n\n/**\n * A component that renders a Vega-Lite chart with SQL data and responsive sizing.\n *\n * The chart can be sized in multiple ways:\n * - Fixed dimensions: Provide both width and height as numbers\n * - Fixed width, proportional height: Provide width as number, height as 'auto'\n * - Fixed height, proportional width: Provide height as number, width as 'auto'\n * - Fully responsive: Leave both as 'auto' (default), chart will fill container while maintaining aspect ratio\n *\n * @param props - The component props\n * @param {number | 'auto'} [props.width='auto'] - The chart width in pixels, or 'auto' to use container width\n * @param {number | 'auto'} [props.height='auto'] - The chart height in pixels, or 'auto' to calculate from aspect ratio\n * @param {number} [props.aspectRatio=3/2] - The desired width-to-height ratio when dimensions are auto-calculated\n * @param {string} props.sqlQuery - The SQL query to fetch data for the chart\n * @param {string | VisualizationSpec} props.spec - The Vega-Lite specification for the chart.\n * Can be either a JSON string or a VisualizationSpec object.\n * The data and size properties will be overridden by the component.\n *\n * @returns The rendered chart component\n *\n * @example\n * // Fixed size chart\n * <VegaLiteChart\n * width={600}\n * height={400}\n * sqlQuery=\"SELECT category, count(*) as count FROM sales GROUP BY category\"\n * spec={{\n * mark: 'bar',\n * encoding: {\n * x: {field: 'category', type: 'nominal'},\n * y: {field: 'count', type: 'quantitative'}\n * }\n * }}\n * />\n *\n * @example\n * // Responsive chart with 16:9 aspect ratio\n * <VegaLiteChart\n * className=\"max-w-[600px]\"\n * aspectRatio={16/9}\n * sqlQuery=\"SELECT date, value FROM metrics\"\n * spec={{\n * mark: 'line',\n * encoding: {\n * x: {field: 'date', type: 'temporal'},\n * y: {field: 'value', type: 'quantitative'}\n * }\n * }}\n * />\n */\nconst VegaLiteSqlChart: React.FC<{\n className?: string;\n width?: number | 'auto';\n height?: number | 'auto';\n aspectRatio?: number;\n sqlQuery: string;\n spec: string | VisualizationSpec;\n dataName?: string;\n lastRunTime?: number;\n isLoading?: boolean;\n}> = ({\n className,\n width = 'auto',\n height = 'auto',\n aspectRatio,\n sqlQuery,\n spec,\n dataName = DEFAULT_DATA_NAME,\n lastRunTime,\n isLoading,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const dimensions = useAspectRatioDimensions({\n containerRef,\n width,\n height,\n aspectRatio,\n });\n const {width: adjustedWidth, height: adjustedHeight} = dimensions;\n const refinedSpec = useMemo(() => {\n const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;\n if (!parsed) return null;\n return {\n ...parsed,\n data: {name: dataName},\n width: adjustedWidth,\n height: adjustedHeight,\n autosize: {\n type: 'fit',\n contains: 'padding',\n },\n } as VisualizationSpec;\n }, [spec, dataName, adjustedWidth, adjustedHeight]);\n\n const result = useSql({query: sqlQuery, version: lastRunTime});\n const arrowTable = result.data?.arrowTable;\n const data = useMemo(() => {\n if (!arrowTable) return null;\n return {queryResult: arrowTableToJson(arrowTable)};\n }, [arrowTable]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\n 'flex h-full w-full flex-col gap-2 overflow-hidden',\n className,\n )}\n >\n {isLoading || result.isLoading ? (\n <div className=\"text-muted-foreground flex items-center justify-center gap-2 p-2\">\n <div className=\"h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600\"></div>\n Running query for chart data…\n </div>\n ) : refinedSpec && data ? (\n <AspectRatio ratio={aspectRatio}>\n <VegaLite spec={refinedSpec} data={data} />\n </AspectRatio>\n ) : result.error ? (\n <div className=\"whitespace-pre-wrap p-2 font-mono text-sm text-red-500\">\n {result.error.message}\n </div>\n ) : null}\n </div>\n );\n};\n\nexport const ArrowChart: React.FC<{\n className?: string;\n width?: number | 'auto';\n height?: number | 'auto';\n aspectRatio?: number;\n spec: string | VisualizationSpec;\n arrowTable: arrow.Table | undefined;\n dataName?: string;\n}> = ({\n className,\n width = 'auto',\n height = 'auto',\n aspectRatio = 3 / 2,\n spec,\n arrowTable,\n dataName = DEFAULT_DATA_NAME,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [chartError, setChartError] = useState<Error | null>(null);\n const dimensions = useAspectRatioDimensions({\n containerRef,\n width,\n height,\n aspectRatio,\n });\n\n const refinedSpec = useMemo(() => {\n const parsed = typeof spec === 'string' ? safeJsonParse(spec) : spec;\n if (!parsed) {\n setChartError(new Error('Invalid Vega-Lite specification'));\n return null;\n }\n return {\n ...parsed,\n data: {name: dataName},\n width: dimensions.width,\n height: dimensions.height,\n autosize: {\n type: 'fit',\n contains: 'padding',\n },\n } as VisualizationSpec;\n }, [spec, dataName, dimensions]);\n\n const data = useMemo(() => {\n if (!arrowTable) return null;\n return {queryResult: arrowTableToJson(arrowTable)};\n }, [arrowTable]);\n\n // Reset chart error whenever spec or data changes\n useEffect(() => {\n setChartError(null);\n }, [spec, data]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\n 'flex h-full w-full flex-col gap-2 overflow-hidden',\n className,\n )}\n >\n {chartError ? (\n <ToolErrorMessage\n error={chartError}\n triggerLabel=\"Chart rendering failed\"\n title=\"Chart error\"\n align=\"start\"\n details={spec}\n />\n ) : (\n refinedSpec &&\n data && (\n <AspectRatio ratio={aspectRatio}>\n <VegaLite spec={refinedSpec} data={data} onError={setChartError} />\n </AspectRatio>\n )\n )}\n </div>\n );\n};\n\nexport const VegaLiteChart = Object.assign(VegaLiteSqlChart, {\n SqlChart: VegaLiteSqlChart,\n ArrowChart: ArrowChart,\n});\n"]}
|