@sqlrooms/vega 0.29.0-rc.9 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,7 +75,11 @@ import {
75
75
  createDefaultAiInstructions,
76
76
  createDefaultAiTools,
77
77
  } from '@sqlrooms/ai';
78
- import {createVegaChartTool} from '@sqlrooms/vega';
78
+ import {
79
+ createVegaChartTool,
80
+ VegaChartToolResult,
81
+ type VegaChartToolResultProps,
82
+ } from '@sqlrooms/vega';
79
83
 
80
84
  // inside your createRoomStore composer
81
85
  createAiSlice({
@@ -95,6 +99,41 @@ createAiSlice({
95
99
  - `editable`: whether users can edit SQL/spec in the chart UI
96
100
  - `editorMode`: which editors to render (`'none' | 'sql' | 'vega' | 'both'`)
97
101
 
102
+ ### AI chart result sizing
103
+
104
+ `VegaChartToolResult` retains its default 16:9 layout when no sizing props are
105
+ provided. Use `height="auto"` to opt into data-driven sizing for
106
+ category-dense horizontal bar charts:
107
+
108
+ ```tsx
109
+ const CategoryAwareChartResult = (props: VegaChartToolResultProps) => (
110
+ <VegaChartToolResult {...props} height="auto" />
111
+ );
112
+
113
+ createAiSlice({
114
+ toolRenderers: {
115
+ chart: CategoryAwareChartResult,
116
+ },
117
+ tools: {
118
+ chart: createVegaChartTool(),
119
+ },
120
+ });
121
+ ```
122
+
123
+ The automatic policy inspects the parsed Vega-Lite encoding and counts distinct
124
+ values in the loaded Arrow data. Horizontal bar charts with at least 12
125
+ categories use `clamp(280, 48 + categoryCount * 22, 800)` pixels; other charts
126
+ continue to use `aspectRatio` (16:9 by default). Transformed and concatenated
127
+ multi-view specs also use the aspect ratio because raw Arrow rows do not reflect
128
+ their rendered data or panel layout. A numeric `height` fixes the outer chart
129
+ viewport while preserving responsive width. Applications can also provide
130
+ `getHeight={({spec, arrowTable}) => ...}` for a custom sizing policy; returning
131
+ `'auto'` delegates to the built-in category-aware policy.
132
+
133
+ Sizing is based on loaded query data rather than an LLM-provided row count, so
134
+ it stays accurate when SQL results or application filters change. The Vega-Lite
135
+ spec remains responsible for visualization semantics, not React layout.
136
+
98
137
  ### LLM invocation / Zod schema fields
99
138
 
100
139
  At runtime, the tool call payload is validated by a Zod schema.
@@ -107,9 +146,12 @@ These fields are supplied by the LLM when invoking the tool (not passed into
107
146
 
108
147
  ## Markdown document image assets
109
148
 
149
+ The host store must include `MarkdownDocumentsSliceState`; image assets are stored
150
+ in `store.markdownDocuments` alongside the document’s Markdown content.
151
+
110
152
  `createChartImageForMarkdownTool(store)` creates an AI-only companion tool that
111
153
  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
154
+ `@sqlrooms/documents` `markdown-document` artifact, and returns a ready-to-insert Markdown
113
155
  image link such as:
114
156
 
115
157
  ```md
@@ -129,5 +171,4 @@ and written into the SVG/PNG as a concrete color. The tool also accepts
129
171
 
130
172
  ## Example apps
131
173
 
132
- - Vega example: https://github.com/sqlrooms/examples/tree/main/vega
133
174
  - AI example (with chart tool): https://github.com/sqlrooms/examples/tree/main/ai
@@ -1,5 +1,5 @@
1
1
  import type { AiSliceState } from '@sqlrooms/ai-core';
2
- import type { DocumentsSliceState } from '@sqlrooms/documents';
2
+ import type { MarkdownDocumentsSliceState } from '@sqlrooms/documents';
3
3
  import { type DuckDbSliceState } from '@sqlrooms/duckdb';
4
4
  import { z } from 'zod';
5
5
  export declare const ChartImageForMarkdownToolParameters: z.ZodObject<{
@@ -40,7 +40,7 @@ export type ChartImageForMarkdownToolOutput = {
40
40
  type StoreLike<S> = {
41
41
  getState: () => S;
42
42
  };
43
- type ChartImageToolState = AiSliceState & DuckDbSliceState & DocumentsSliceState;
43
+ type ChartImageToolState = AiSliceState & DuckDbSliceState & MarkdownDocumentsSliceState;
44
44
  export declare function createChartImageForMarkdownTool<S extends ChartImageToolState = ChartImageToolState>(store: StoreLike<S>): import("ai").Tool<{
45
45
  documentArtifactId: string;
46
46
  format: "svg" | "png";
@@ -1 +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"}
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,2BAA2B,EAAC,MAAM,qBAAqB,CAAC;AACrE,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,2BAA2B,CAAC;AAE9B,wBAAgB,+BAA+B,CAC7C,CAAC,SAAS,mBAAmB,GAAG,mBAAmB,EACnD,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4FpB"}
@@ -85,7 +85,9 @@ For portable Markdown documents, the default renderTheme is light with a white b
85
85
  const timestamp = Date.now();
86
86
  const assetId = params.assetId ?? createChartAssetId(params, source.sourceToolCallId);
87
87
  const filename = `${assetId}.${params.format}`;
88
- store.getState().documents.upsertAsset(params.documentArtifactId, {
88
+ store
89
+ .getState()
90
+ .markdownDocuments.upsertAsset(params.documentArtifactId, {
89
91
  id: assetId,
90
92
  mediaType: image.mediaType,
91
93
  encoding: image.encoding,
@@ -1 +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: `![${escapeMarkdownAlt(params.alt)}](asset://${encodeURIComponent(assetId)})`,\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
+ {"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 {MarkdownDocumentsSliceState} 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 MarkdownDocumentsSliceState;\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\n .getState()\n .markdownDocuments.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: `![${escapeMarkdownAlt(params.alt)}](asset://${encodeURIComponent(assetId)})`,\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"]}
@@ -3,6 +3,7 @@ import { ReactNode } from 'react';
3
3
  import { EmbedOptions } from 'vega-embed';
4
4
  import { EditorMode } from './editor/types';
5
5
  import type { VegaChartToolOutput, VegaChartToolParameters } from './VegaChartTool';
6
+ import type { VegaChartHeight, VegaChartHeightResolver } from './chartSizing';
6
7
  export type VegaChartToolResultProps = ToolRendererProps<VegaChartToolOutput, VegaChartToolParameters> & {
7
8
  className?: string;
8
9
  options?: EmbedOptions;
@@ -11,10 +12,25 @@ export type VegaChartToolResultProps = ToolRendererProps<VegaChartToolOutput, Ve
11
12
  * @default 'both'
12
13
  */
13
14
  editorMode?: EditorMode;
15
+ /**
16
+ * Width-to-height ratio used when no fixed or automatic height is selected.
17
+ * @default 16/9
18
+ */
19
+ aspectRatio?: number;
20
+ /**
21
+ * Fixed outer chart height, or `'auto'` to opt into category-aware sizing.
22
+ * Automatic sizing falls back to the aspect ratio for ordinary charts.
23
+ */
24
+ height?: VegaChartHeight;
25
+ /**
26
+ * Optional application-specific height policy. Returning `'auto'` applies
27
+ * the built-in category-aware sizing policy.
28
+ */
29
+ getHeight?: VegaChartHeightResolver;
14
30
  };
15
31
  /**
16
32
  * Renders a chart tool call with visualization using Vega-Lite.
17
33
  * Shows read-only editors for inspecting spec and SQL.
18
34
  */
19
- export declare function VegaChartToolResult({ className, input, output, options, editorMode, }: VegaChartToolResultProps): ReactNode;
35
+ export declare function VegaChartToolResult({ className, input, output, options, editorMode, aspectRatio, height, getHeight, }: VegaChartToolResultProps): ReactNode;
20
36
  //# 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,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"}
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;AAIzB,OAAO,KAAK,EAAC,eAAe,EAAE,uBAAuB,EAAC,MAAM,eAAe,CAAC;AAE5E,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;IACxB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,uBAAuB,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,KAAK,EACL,MAAM,EACN,OAAO,EACP,UAAmB,EACnB,WAAoB,EACpB,MAAM,EACN,SAAS,GACV,EAAE,wBAAwB,GAAG,SAAS,CAmCtC"}
@@ -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, input, output, options, editorMode = 'both', }) {
12
+ export function VegaChartToolResult({ className, input, output, options, editorMode = 'both', aspectRatio = 16 / 9, height, getHeight, }) {
13
13
  const sqlQuery = output?.sqlQuery ?? '';
14
14
  const vegaLiteSpec = output?.vegaLiteSpec;
15
15
  if (!vegaLiteSpec) {
16
16
  return null;
17
17
  }
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 })] }) }) }) })] }));
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", 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: aspectRatio, height: height, getHeight: getHeight, 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;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
+ {"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;AA+BxD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,SAAS,EACT,KAAK,EACL,MAAM,EACN,OAAO,EACP,UAAU,GAAG,MAAM,EACnB,WAAW,GAAG,EAAE,GAAG,CAAC,EACpB,MAAM,EACN,SAAS,GACgB;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,iCAAiC,YAAE,KAAK,CAAC,SAAS,GAAK,CACrE,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,IACf,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,SAAS,EAAC,MAAM,YAEhB,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';\nimport type {VegaChartHeight, VegaChartHeightResolver} from './chartSizing';\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 * Width-to-height ratio used when no fixed or automatic height is selected.\n * @default 16/9\n */\n aspectRatio?: number;\n /**\n * Fixed outer chart height, or `'auto'` to opt into category-aware sizing.\n * Automatic sizing falls back to the aspect ratio for ordinary charts.\n */\n height?: VegaChartHeight;\n /**\n * Optional application-specific height policy. Returning `'auto'` applies\n * the built-in category-aware sizing policy.\n */\n getHeight?: VegaChartHeightResolver;\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 aspectRatio = 16 / 9,\n height,\n getHeight,\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\">{input.reasoning}</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\n aspectRatio={aspectRatio}\n height={height}\n getHeight={getHeight}\n className=\"pt-2\"\n >\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;AA0HD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,kBAAkB;IAC7B;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;CAEH,CAAC"}
1
+ {"version":3,"file":"VegaLiteArrowChart.d.ts","sourceRoot":"","sources":["../src/VegaLiteArrowChart.tsx"],"names":[],"mappings":"AAIA,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;AA8ID;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,kBAAkB;IAC7B;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;CAEH,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ToolErrorMessage } from '@sqlrooms/ai-core';
3
3
  import { arrowTableToJson } from '@sqlrooms/duckdb';
4
- import { AspectRatio, cn, useAspectRatioDimensions, useTheme, } from '@sqlrooms/ui';
4
+ import { cn, useAspectRatioDimensions, useTheme } from '@sqlrooms/ui';
5
5
  import { safeJsonParse } from '@sqlrooms/utils';
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
7
  import { useVegaEmbed } from 'react-vega';
@@ -27,7 +27,11 @@ export function makeDefaultVegaLiteOptions(options) {
27
27
  ...options,
28
28
  };
29
29
  }
30
- const VegaLiteArrowChartBase = ({ className, aspectRatio = 16 / 9, spec, arrowTable, options: propsOptions, width = 'auto', height = 'auto', children, }) => {
30
+ const VegaLiteArrowChartBase = (props) => {
31
+ const { className, spec, arrowTable, options: propsOptions, width = 'auto', height = 'auto', children, } = props;
32
+ const aspectRatio = Object.hasOwn(props, 'aspectRatio')
33
+ ? props.aspectRatio
34
+ : 16 / 9;
31
35
  const { theme } = useTheme();
32
36
  const options = useMemo(() => makeDefaultVegaLiteOptions({
33
37
  config: {
@@ -81,7 +85,7 @@ const VegaLiteArrowChartBase = ({ className, aspectRatio = 16 / 9, spec, arrowTa
81
85
  containerRef,
82
86
  width,
83
87
  height,
84
- aspectRatio,
88
+ aspectRatio: height === 'auto' || width !== 'auto' ? aspectRatio : undefined,
85
89
  });
86
90
  const changeDimensions = useCallback((width, height) => {
87
91
  embed?.view.width(width).height(height).runAsync();
@@ -89,8 +93,15 @@ const VegaLiteArrowChartBase = ({ className, aspectRatio = 16 / 9, spec, arrowTa
89
93
  useEffect(() => {
90
94
  changeDimensions(dimensions.width, dimensions.height);
91
95
  }, [changeDimensions, dimensions.width, dimensions.height]);
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 &&
93
- data && (_jsx(AspectRatio, { ratio: aspectRatio, className: "overflow-visible", asChild: true, children: _jsx("div", { ref: ref, className: "[&_svg]:overflow-visible" }) }))) }), children] }) }));
96
+ return (_jsx(VegaChartContextProvider, { value: { embed }, children: _jsxs("div", { className: cn('relative flex w-full flex-col gap-2', className), children: [_jsx("div", { className: cn('peer relative', height === 'auto' && aspectRatio === undefined && 'h-full'), children: displayError ? (_jsx(ToolErrorMessage, { error: displayError, triggerLabel: "Chart rendering failed", title: "Chart error", align: "start", details: spec })) : (specWithData &&
97
+ data && (_jsx("div", { ref: containerRef, style: {
98
+ ...(height === 'auto' && aspectRatio !== undefined
99
+ ? { aspectRatio }
100
+ : height !== 'auto'
101
+ ? { height }
102
+ : {}),
103
+ ...(width !== 'auto' ? { width } : {}),
104
+ }, className: cn('overflow-visible', width === 'auto' && 'w-full', height === 'auto' && aspectRatio === undefined && 'h-full'), children: _jsx("div", { ref: ref, className: "h-full w-full [&_svg]:overflow-visible" }) }))) }), children] }) }));
94
105
  };
95
106
  /**
96
107
  * Composable Vega-Lite chart component with support for custom actions.
@@ -1 +1 @@
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
+ {"version":3,"file":"VegaLiteArrowChart.js","sourceRoot":"","sources":["../src/VegaLiteArrowChart.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ToolErrorMessage} from '@sqlrooms/ai-core';\nimport {arrowTableToJson} from '@sqlrooms/duckdb';\nimport {cn, useAspectRatioDimensions, useTheme} 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> = (props) => {\n const {\n className,\n spec,\n arrowTable,\n options: propsOptions,\n width = 'auto',\n height = 'auto',\n children,\n } = props;\n const aspectRatio = Object.hasOwn(props, 'aspectRatio')\n ? props.aspectRatio\n : 16 / 9;\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 height === 'auto' || width !== 'auto' ? aspectRatio : undefined,\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 className={cn('relative flex w-full flex-col gap-2', className)}>\n <div\n className={cn(\n 'peer relative',\n height === 'auto' && aspectRatio === undefined && 'h-full',\n )}\n >\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 <div\n ref={containerRef}\n style={{\n ...(height === 'auto' && aspectRatio !== undefined\n ? {aspectRatio}\n : height !== 'auto'\n ? {height}\n : {}),\n ...(width !== 'auto' ? {width} : {}),\n }}\n className={cn(\n 'overflow-visible',\n width === 'auto' && 'w-full',\n height === 'auto' && aspectRatio === undefined && 'h-full',\n )}\n >\n <div\n ref={ref}\n className=\"h-full w-full [&_svg]:overflow-visible\"\n />\n </div>\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"]}
@@ -0,0 +1,20 @@
1
+ import type * as arrow from 'apache-arrow';
2
+ import type { VisualizationSpec } from 'vega-embed';
3
+ /** Height behavior for a Vega chart result. */
4
+ export type VegaChartHeight = number | 'auto';
5
+ /** Data available when resolving the height of a rendered Vega chart. */
6
+ export interface VegaChartSizingContext {
7
+ /** The parsed, responsive Vega-Lite specification. */
8
+ spec: VisualizationSpec;
9
+ /** The data loaded for the chart, when available. */
10
+ arrowTable: arrow.Table | undefined;
11
+ }
12
+ /** Resolves a chart height from its parsed specification and loaded data. */
13
+ export type VegaChartHeightResolver = (context: VegaChartSizingContext) => VegaChartHeight;
14
+ /**
15
+ * Computes a taller height for category-dense horizontal bar charts.
16
+ *
17
+ * Returns `'auto'` for other chart shapes so they retain aspect-ratio sizing.
18
+ */
19
+ export declare function getCategoryAwareVegaChartHeight({ spec, arrowTable, }: VegaChartSizingContext): VegaChartHeight;
20
+ //# sourceMappingURL=chartSizing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chartSizing.d.ts","sourceRoot":"","sources":["../src/chartSizing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAElD,+CAA+C;AAC/C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C,yEAAyE;AACzE,MAAM,WAAW,sBAAsB;IACrC,sDAAsD;IACtD,IAAI,EAAE,iBAAiB,CAAC;IACxB,qDAAqD;IACrD,UAAU,EAAE,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;CACrC;AAED,6EAA6E;AAC7E,MAAM,MAAM,uBAAuB,GAAG,CACpC,OAAO,EAAE,sBAAsB,KAC5B,eAAe,CAAC;AA8GrB;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,EAC9C,IAAI,EACJ,UAAU,GACX,EAAE,sBAAsB,GAAG,eAAe,CAmB1C"}
@@ -0,0 +1,103 @@
1
+ const MIN_CATEGORY_COUNT = 12;
2
+ const MIN_AUTO_HEIGHT = 280;
3
+ const MAX_AUTO_HEIGHT = 800;
4
+ const CHART_VERTICAL_PADDING = 48;
5
+ const CATEGORY_ROW_HEIGHT = 22;
6
+ const MAX_MEANINGFUL_CATEGORY_COUNT = Math.ceil((MAX_AUTO_HEIGHT - CHART_VERTICAL_PADDING) / CATEGORY_ROW_HEIGHT);
7
+ /** Returns a JSON object value while excluding arrays and primitives. */
8
+ function asObject(value) {
9
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
10
+ ? value
11
+ : undefined;
12
+ }
13
+ /** Extracts the Vega-Lite mark type from string and object mark definitions. */
14
+ function getMarkType(spec) {
15
+ if (typeof spec.mark === 'string') {
16
+ return spec.mark;
17
+ }
18
+ return asObject(spec.mark)?.type;
19
+ }
20
+ /** Checks for data/layout semantics that raw-table sizing cannot reproduce. */
21
+ function hasUnsupportedAutoSizingStructure(spec) {
22
+ if (Array.isArray(spec.transform) && spec.transform.length > 0) {
23
+ return true;
24
+ }
25
+ for (const key of ['concat', 'hconcat', 'vconcat']) {
26
+ if (Array.isArray(spec[key]))
27
+ return true;
28
+ }
29
+ const layers = spec.layer;
30
+ if (Array.isArray(layers) &&
31
+ layers.some((layer) => hasUnsupportedAutoSizingStructure(asObject(layer) ?? {}))) {
32
+ return true;
33
+ }
34
+ const nestedSpec = asObject(spec.spec);
35
+ return nestedSpec ? hasUnsupportedAutoSizingStructure(nestedSpec) : false;
36
+ }
37
+ /** Finds the discrete y field of a horizontal bar unit in a nested spec. */
38
+ function getHorizontalBarCategoryField(spec, inheritedEncoding = {}) {
39
+ const encoding = { ...inheritedEncoding, ...asObject(spec.encoding) };
40
+ const x = asObject(encoding.x);
41
+ const y = asObject(encoding.y);
42
+ const isAggregate = typeof x?.aggregate === 'string';
43
+ const isQuantitativeX = x?.type === 'quantitative' || isAggregate;
44
+ const isDiscreteY = y?.type === 'nominal' ||
45
+ y?.type === 'ordinal' ||
46
+ (isAggregate && y?.type === undefined);
47
+ if (getMarkType(spec) === 'bar' &&
48
+ isQuantitativeX &&
49
+ isDiscreteY &&
50
+ typeof y?.field === 'string') {
51
+ return y.field;
52
+ }
53
+ for (const key of ['layer', 'concat', 'hconcat', 'vconcat']) {
54
+ const children = spec[key];
55
+ if (!Array.isArray(children))
56
+ continue;
57
+ for (const child of children) {
58
+ const field = getHorizontalBarCategoryField(asObject(child) ?? {}, encoding);
59
+ if (field)
60
+ return field;
61
+ }
62
+ }
63
+ const nestedSpec = asObject(spec.spec);
64
+ return nestedSpec
65
+ ? getHorizontalBarCategoryField(nestedSpec, encoding)
66
+ : undefined;
67
+ }
68
+ /** Counts distinct non-null field values up to the height saturation point. */
69
+ function countDistinctValues(table, field) {
70
+ const vector = table.getChild(field);
71
+ if (!vector)
72
+ return 0;
73
+ const values = new Set();
74
+ for (let index = 0; index < table.numRows; index += 1) {
75
+ const value = vector.get(index);
76
+ if (value !== null && value !== undefined) {
77
+ values.add(`${typeof value}:${String(value)}`);
78
+ if (values.size >= MAX_MEANINGFUL_CATEGORY_COUNT)
79
+ break;
80
+ }
81
+ }
82
+ return values.size;
83
+ }
84
+ /**
85
+ * Computes a taller height for category-dense horizontal bar charts.
86
+ *
87
+ * Returns `'auto'` for other chart shapes so they retain aspect-ratio sizing.
88
+ */
89
+ export function getCategoryAwareVegaChartHeight({ spec, arrowTable, }) {
90
+ if (!arrowTable)
91
+ return 'auto';
92
+ const specObject = asObject(spec) ?? {};
93
+ if (hasUnsupportedAutoSizingStructure(specObject))
94
+ return 'auto';
95
+ const categoryField = getHorizontalBarCategoryField(specObject);
96
+ if (!categoryField)
97
+ return 'auto';
98
+ const categoryCount = countDistinctValues(arrowTable, categoryField);
99
+ if (categoryCount < MIN_CATEGORY_COUNT)
100
+ return 'auto';
101
+ return Math.min(MAX_AUTO_HEIGHT, Math.max(MIN_AUTO_HEIGHT, CHART_VERTICAL_PADDING + categoryCount * CATEGORY_ROW_HEIGHT));
102
+ }
103
+ //# sourceMappingURL=chartSizing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chartSizing.js","sourceRoot":"","sources":["../src/chartSizing.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as arrow from 'apache-arrow';\nimport type {VisualizationSpec} from 'vega-embed';\n\n/** Height behavior for a Vega chart result. */\nexport type VegaChartHeight = number | 'auto';\n\n/** Data available when resolving the height of a rendered Vega chart. */\nexport interface VegaChartSizingContext {\n /** The parsed, responsive Vega-Lite specification. */\n spec: VisualizationSpec;\n /** The data loaded for the chart, when available. */\n arrowTable: arrow.Table | undefined;\n}\n\n/** Resolves a chart height from its parsed specification and loaded data. */\nexport type VegaChartHeightResolver = (\n context: VegaChartSizingContext,\n) => VegaChartHeight;\n\nconst MIN_CATEGORY_COUNT = 12;\nconst MIN_AUTO_HEIGHT = 280;\nconst MAX_AUTO_HEIGHT = 800;\nconst CHART_VERTICAL_PADDING = 48;\nconst CATEGORY_ROW_HEIGHT = 22;\nconst MAX_MEANINGFUL_CATEGORY_COUNT = Math.ceil(\n (MAX_AUTO_HEIGHT - CHART_VERTICAL_PADDING) / CATEGORY_ROW_HEIGHT,\n);\n\ntype JsonObject = Record<string, unknown>;\n\n/** Returns a JSON object value while excluding arrays and primitives. */\nfunction asObject(value: unknown): JsonObject | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as JsonObject)\n : undefined;\n}\n\n/** Extracts the Vega-Lite mark type from string and object mark definitions. */\nfunction getMarkType(spec: JsonObject): string | undefined {\n if (typeof spec.mark === 'string') {\n return spec.mark;\n }\n return asObject(spec.mark)?.type as string | undefined;\n}\n\n/** Checks for data/layout semantics that raw-table sizing cannot reproduce. */\nfunction hasUnsupportedAutoSizingStructure(spec: JsonObject): boolean {\n if (Array.isArray(spec.transform) && spec.transform.length > 0) {\n return true;\n }\n\n for (const key of ['concat', 'hconcat', 'vconcat'] as const) {\n if (Array.isArray(spec[key])) return true;\n }\n\n const layers = spec.layer;\n if (\n Array.isArray(layers) &&\n layers.some((layer) =>\n hasUnsupportedAutoSizingStructure(asObject(layer) ?? {}),\n )\n ) {\n return true;\n }\n\n const nestedSpec = asObject(spec.spec);\n return nestedSpec ? hasUnsupportedAutoSizingStructure(nestedSpec) : false;\n}\n\n/** Finds the discrete y field of a horizontal bar unit in a nested spec. */\nfunction getHorizontalBarCategoryField(\n spec: JsonObject,\n inheritedEncoding: JsonObject = {},\n): string | undefined {\n const encoding = {...inheritedEncoding, ...asObject(spec.encoding)};\n const x = asObject(encoding.x);\n const y = asObject(encoding.y);\n const isAggregate = typeof x?.aggregate === 'string';\n const isQuantitativeX = x?.type === 'quantitative' || isAggregate;\n const isDiscreteY =\n y?.type === 'nominal' ||\n y?.type === 'ordinal' ||\n (isAggregate && y?.type === undefined);\n\n if (\n getMarkType(spec) === 'bar' &&\n isQuantitativeX &&\n isDiscreteY &&\n typeof y?.field === 'string'\n ) {\n return y.field;\n }\n\n for (const key of ['layer', 'concat', 'hconcat', 'vconcat'] as const) {\n const children = spec[key];\n if (!Array.isArray(children)) continue;\n for (const child of children) {\n const field = getHorizontalBarCategoryField(\n asObject(child) ?? {},\n encoding,\n );\n if (field) return field;\n }\n }\n\n const nestedSpec = asObject(spec.spec);\n return nestedSpec\n ? getHorizontalBarCategoryField(nestedSpec, encoding)\n : undefined;\n}\n\n/** Counts distinct non-null field values up to the height saturation point. */\nfunction countDistinctValues(table: arrow.Table, field: string): number {\n const vector = table.getChild(field);\n if (!vector) return 0;\n\n const values = new Set<string>();\n for (let index = 0; index < table.numRows; index += 1) {\n const value = vector.get(index);\n if (value !== null && value !== undefined) {\n values.add(`${typeof value}:${String(value)}`);\n if (values.size >= MAX_MEANINGFUL_CATEGORY_COUNT) break;\n }\n }\n return values.size;\n}\n\n/**\n * Computes a taller height for category-dense horizontal bar charts.\n *\n * Returns `'auto'` for other chart shapes so they retain aspect-ratio sizing.\n */\nexport function getCategoryAwareVegaChartHeight({\n spec,\n arrowTable,\n}: VegaChartSizingContext): VegaChartHeight {\n if (!arrowTable) return 'auto';\n\n const specObject = asObject(spec) ?? {};\n if (hasUnsupportedAutoSizingStructure(specObject)) return 'auto';\n\n const categoryField = getHorizontalBarCategoryField(specObject);\n if (!categoryField) return 'auto';\n\n const categoryCount = countDistinctValues(arrowTable, categoryField);\n if (categoryCount < MIN_CATEGORY_COUNT) return 'auto';\n\n return Math.min(\n MAX_AUTO_HEIGHT,\n Math.max(\n MIN_AUTO_HEIGHT,\n CHART_VERTICAL_PADDING + categoryCount * CATEGORY_ROW_HEIGHT,\n ),\n );\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import type { VegaChartHeight, VegaChartHeightResolver } from '../chartSizing';
2
3
  export interface VegaChartDisplayProps {
3
4
  /**
4
5
  * Custom class name for the chart container
@@ -9,6 +10,14 @@ export interface VegaChartDisplayProps {
9
10
  * @default 16/9
10
11
  */
11
12
  aspectRatio?: number;
13
+ /**
14
+ * Fixed outer chart height, or `'auto'` for category-aware sizing.
15
+ */
16
+ height?: VegaChartHeight;
17
+ /**
18
+ * Optional height policy evaluated with the parsed spec and loaded data.
19
+ */
20
+ getHeight?: VegaChartHeightResolver;
12
21
  /**
13
22
  * Children passed through to VegaLiteArrowChart (e.g., action components)
14
23
  */
@@ -1 +1 @@
1
- {"version":3,"file":"VegaChartDisplay.d.ts","sourceRoot":"","sources":["../../src/editor/VegaChartDisplay.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAkE5D,CAAC"}
1
+ {"version":3,"file":"VegaChartDisplay.d.ts","sourceRoot":"","sources":["../../src/editor/VegaChartDisplay.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAC,eAAe,EAAE,uBAAuB,EAAC,MAAM,gBAAgB,CAAC;AAG7E,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB;;OAEG;IACH,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA6E5D,CAAC"}
@@ -2,7 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ToolErrorMessage } from '@sqlrooms/ai-core';
3
3
  import { useSql } from '@sqlrooms/duckdb';
4
4
  import { cn } from '@sqlrooms/ui';
5
+ import React from 'react';
5
6
  import { VegaLiteArrowChart } from '../VegaLiteArrowChart';
7
+ import { getCategoryAwareVegaChartHeight } from '../chartSizing';
6
8
  import { useVegaEditorContext } from './VegaEditorContext';
7
9
  /**
8
10
  * Chart display subcomponent for VegaLiteChart.Container.
@@ -19,7 +21,7 @@ import { useVegaEditorContext } from './VegaEditorContext';
19
21
  * </VegaLiteChart.Container>
20
22
  * ```
21
23
  */
22
- export const VegaChartDisplay = ({ className, aspectRatio = 16 / 9, children, }) => {
24
+ export const VegaChartDisplay = ({ className, aspectRatio = 16 / 9, height, getHeight, children, }) => {
23
25
  const { state, arrowTable, options } = useVegaEditorContext();
24
26
  // Use the applied SQL for chart rendering (updates when Apply is clicked)
25
27
  const sqlQuery = state.appliedSql;
@@ -33,6 +35,13 @@ export const VegaChartDisplay = ({ className, aspectRatio = 16 / 9, children, })
33
35
  // Use parsed spec for live preview, fall back to last valid spec if parse failed
34
36
  // This ensures the chart keeps rendering during typing even with invalid JSON
35
37
  const spec = state.parsedSpec ?? state.lastValidSpec;
38
+ const resolvedHeight = React.useMemo(() => {
39
+ const context = { spec, arrowTable: chartData };
40
+ const requestedHeight = getHeight?.(context) ?? height;
41
+ return requestedHeight === 'auto'
42
+ ? getCategoryAwareVegaChartHeight(context)
43
+ : requestedHeight;
44
+ }, [chartData, getHeight, height, spec]);
36
45
  // Show loading state
37
46
  if (sqlQuery && !arrowTable && sqlResult.isLoading) {
38
47
  return (_jsxs("div", { className: cn('text-muted-foreground flex items-center justify-center', className), children: [_jsx("div", { className: "h-4 w-4 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600" }), _jsx("span", { className: "ml-2 text-sm", children: "Loading data..." })] }));
@@ -41,6 +50,6 @@ export const VegaChartDisplay = ({ className, aspectRatio = 16 / 9, children, })
41
50
  if (sqlQuery && !arrowTable && sqlResult.error) {
42
51
  return (_jsx("div", { className: className, children: _jsx(ToolErrorMessage, { error: sqlResult.error, triggerLabel: "SQL query failed", title: "Query error", align: "start", details: sqlQuery }) }));
43
52
  }
44
- return (_jsx("div", { className: cn('relative', className), children: _jsx(VegaLiteArrowChart, { className: "w-full", spec: spec, arrowTable: chartData, aspectRatio: aspectRatio, options: options, children: children }) }));
53
+ return (_jsx("div", { className: cn('relative', className), children: _jsx(VegaLiteArrowChart, { className: "w-full", spec: spec, arrowTable: chartData, aspectRatio: aspectRatio, height: resolvedHeight, options: options, children: children }) }));
45
54
  };
46
55
  //# sourceMappingURL=VegaChartDisplay.js.map
@@ -1 +1 @@
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"]}
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;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAC,kBAAkB,EAAC,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAC,+BAA+B,EAAC,MAAM,gBAAgB,CAAC;AAE/D,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AA0BzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,SAAS,EACT,WAAW,GAAG,EAAE,GAAG,CAAC,EACpB,MAAM,EACN,SAAS,EACT,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,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACxC,MAAM,OAAO,GAAG,EAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAC,CAAC;QAC9C,MAAM,eAAe,GAAG,SAAS,EAAE,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC;QACvD,OAAO,eAAe,KAAK,MAAM;YAC/B,CAAC,CAAC,+BAA+B,CAAC,OAAO,CAAC;YAC1C,CAAC,CAAC,eAAe,CAAC;IACtB,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAEzC,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,MAAM,EAAE,cAAc,EACtB,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 {getCategoryAwareVegaChartHeight} from '../chartSizing';\nimport type {VegaChartHeight, VegaChartHeightResolver} from '../chartSizing';\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 * Fixed outer chart height, or `'auto'` for category-aware sizing.\n */\n height?: VegaChartHeight;\n /**\n * Optional height policy evaluated with the parsed spec and loaded data.\n */\n getHeight?: VegaChartHeightResolver;\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 height,\n getHeight,\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 const resolvedHeight = React.useMemo(() => {\n const context = {spec, arrowTable: chartData};\n const requestedHeight = getHeight?.(context) ?? height;\n return requestedHeight === 'auto'\n ? getCategoryAwareVegaChartHeight(context)\n : requestedHeight;\n }, [chartData, getHeight, height, spec]);\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 height={resolvedHeight}\n options={options}\n >\n {children}\n </VegaLiteArrowChart>\n </div>\n );\n};\n"]}
package/dist/index.d.ts CHANGED
@@ -3,6 +3,9 @@
3
3
  * @packageDocumentation
4
4
  */
5
5
  export { VegaChartToolResult as VegaChartToolResult } from './VegaChartToolResult';
6
+ export type { VegaChartToolResultProps } from './VegaChartToolResult';
7
+ export { getCategoryAwareVegaChartHeight } from './chartSizing';
8
+ export type { VegaChartHeight, VegaChartHeightResolver, VegaChartSizingContext, } from './chartSizing';
6
9
  export type { VisualizationSpec } from 'vega-embed';
7
10
  export type { SignalListenerHandler, SignalValue } from 'vega';
8
11
  export { createVegaChartTool, createSqlValidator, VegaChartToolParameters, DEFAULT_VEGA_CHART_DESCRIPTION, } from './VegaChartTool';
@@ -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;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"}
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,wBAAwB,EAAC,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAC,+BAA+B,EAAC,MAAM,eAAe,CAAC;AAC9D,YAAY,EACV,eAAe,EACf,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,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
@@ -10,6 +10,7 @@ import { VegaSpecEditorPanel } from './editor/VegaSpecEditorPanel';
10
10
  import { VegaSqlEditorPanel } from './editor/VegaSqlEditorPanel';
11
11
  import { VegaChartEditorActions } from './editor/VegaChartEditorActions';
12
12
  export { VegaChartToolResult as VegaChartToolResult } from './VegaChartToolResult';
13
+ export { getCategoryAwareVegaChartHeight } from './chartSizing';
13
14
  export { createVegaChartTool, createSqlValidator, VegaChartToolParameters, DEFAULT_VEGA_CHART_DESCRIPTION, } from './VegaChartTool';
14
15
  export { createChartImageForMarkdownTool, ChartImageForMarkdownToolParameters, } from './ChartImageForMarkdownTool';
15
16
  /**
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;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"]}
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;AAEjF,OAAO,EAAC,+BAA+B,EAAC,MAAM,eAAe,CAAC;AAS9D,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 {VegaChartToolResultProps} from './VegaChartToolResult';\nexport {getCategoryAwareVegaChartHeight} from './chartSizing';\nexport type {\n VegaChartHeight,\n VegaChartHeightResolver,\n VegaChartSizingContext,\n} from './chartSizing';\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,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlrooms/vega",
3
- "version": "0.29.0-rc.9",
3
+ "version": "0.29.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/sqlrooms/sqlrooms.git"
@@ -19,19 +19,21 @@
19
19
  "build": "tsc",
20
20
  "dev": "tsc -w",
21
21
  "lint": "eslint .",
22
+ "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest",
23
+ "test:watch": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --watch",
22
24
  "typecheck": "tsc --noEmit",
23
25
  "typedoc": "typedoc"
24
26
  },
25
27
  "dependencies": {
26
- "@sqlrooms/ai-core": "0.29.0-rc.9",
27
- "@sqlrooms/codemirror": "0.29.0-rc.9",
28
- "@sqlrooms/documents": "0.29.0-rc.9",
29
- "@sqlrooms/duckdb": "0.29.0-rc.9",
30
- "@sqlrooms/monaco-editor": "0.29.0-rc.9",
31
- "@sqlrooms/sql-editor": "0.29.0-rc.9",
32
- "@sqlrooms/ui": "0.29.0-rc.9",
33
- "@sqlrooms/utils": "0.29.0-rc.9",
34
- "ai": "^6.0.154",
28
+ "@sqlrooms/ai-core": "0.29.0",
29
+ "@sqlrooms/codemirror": "0.29.0",
30
+ "@sqlrooms/documents": "0.29.0",
31
+ "@sqlrooms/duckdb": "0.29.0",
32
+ "@sqlrooms/monaco-editor": "0.29.0",
33
+ "@sqlrooms/sql-editor": "0.29.0",
34
+ "@sqlrooms/ui": "0.29.0",
35
+ "@sqlrooms/utils": "0.29.0",
36
+ "ai": "^6.0.177",
35
37
  "lucide-react": "^0.556.0",
36
38
  "react-vega": "^8.0.0",
37
39
  "vega": "^6.2.0",
@@ -40,7 +42,10 @@
40
42
  "zod": "^4.1.8"
41
43
  },
42
44
  "devDependencies": {
43
- "@monaco-editor/react": "^4.7.0"
45
+ "@monaco-editor/react": "^4.7.0",
46
+ "@types/jest": "^30.0.0",
47
+ "jest": "^30.1.3",
48
+ "ts-jest": "^29.4.4"
44
49
  },
45
50
  "peerDependencies": {
46
51
  "apache-arrow": "17.0.0",
@@ -50,5 +55,5 @@
50
55
  "publishConfig": {
51
56
  "access": "public"
52
57
  },
53
- "gitHead": "e9e4ed45ebb0ceb3e1e647674a7039b8d9325583"
58
+ "gitHead": "e32002430a308284ef021c9345aeb12fdc87ff42"
54
59
  }