@sqlrooms/vega 0.27.0 → 0.28.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
@@ -1,204 +1,110 @@
1
- A package that provides Vega-Lite visualization components for the SQLRooms framework, allowing you to create interactive data visualizations directly from SQL queries.
1
+ Vega-Lite chart components and AI chart tool integration for SQLRooms.
2
2
 
3
- ## About Vega-Lite
4
-
5
- [Vega-Lite](https://vega.github.io/vega-lite/) is a high-level grammar of interactive graphics. It provides a concise, declarative JSON syntax to create an expressive range of visualizations for data analysis and presentation.
6
-
7
- Vega-Lite specifications describe visualizations as encoding mappings from data to properties of graphical marks (e.g., points or bars). The Vega-Lite compiler automatically produces visualization components including axes, legends, and scales. This approach allows for concise specifications while giving users control to customize various parts of a visualization.
8
-
9
- This package integrates Vega-Lite with SQLRooms, allowing you to easily create data visualizations directly from SQL queries.
10
-
11
- ## Components
12
-
13
- ### VegaLiteChart
14
-
15
- A React component that renders a Vega-Lite chart with SQL data and responsive sizing.
16
-
17
- #### Features
18
-
19
- - **SQL-Powered**: Directly use SQL queries to fetch data for your visualizations
20
- - **Responsive Design**: Multiple sizing options to fit any layout
21
- - **Aspect Ratio Control**: Maintain visual consistency with customizable aspect ratios
22
- - **Integration with DuckDB**: Seamlessly works with the SQLRooms DuckDB integration
23
-
24
- #### Installation
3
+ ## Installation
25
4
 
26
5
  ```bash
27
- npm install @sqlrooms/vega
28
- # or
29
- yarn add @sqlrooms/vega
6
+ npm install @sqlrooms/vega @sqlrooms/duckdb @sqlrooms/ui
30
7
  ```
31
8
 
32
- #### Usage
9
+ ## Main exports
10
+
11
+ - `VegaLiteChart` (simple + compound component API)
12
+ - `createVegaChartTool()` for AI tool workflows
13
+ - `VegaChartToolResult`
14
+ - editor utilities/hooks (`useVegaChartEditor`, `useVegaEditorContext`)
15
+
16
+ ## Quick start (simple chart)
33
17
 
34
18
  ```tsx
35
19
  import {VegaLiteChart} from '@sqlrooms/vega';
36
20
 
37
- // Basic usage
38
- function MyChart() {
21
+ export function SalesChart() {
39
22
  return (
40
23
  <VegaLiteChart
41
- sqlQuery="SELECT category, count(*) as count FROM sales GROUP BY category"
24
+ sqlQuery="SELECT category, SUM(amount) AS total FROM sales GROUP BY category"
42
25
  spec={{
43
26
  mark: 'bar',
44
27
  encoding: {
45
28
  x: {field: 'category', type: 'nominal'},
46
- y: {field: 'count', type: 'quantitative'},
29
+ y: {field: 'total', type: 'quantitative'},
47
30
  },
48
31
  }}
32
+ aspectRatio={16 / 9}
49
33
  />
50
34
  );
51
35
  }
52
36
  ```
53
37
 
54
- #### Props
55
-
56
- | Prop | Type | Default | Description |
57
- | ------------- | ----------------------------- | ----------- | --------------------------------------------------------------------- |
58
- | `sqlQuery` | `string` | (required) | The SQL query to fetch data for the chart |
59
- | `spec` | `string \| VisualizationSpec` | (required) | The Vega-Lite specification for the chart |
60
- | `width` | `number \| 'auto'` | `'auto'` | The chart width in pixels, or 'auto' to use container width |
61
- | `height` | `number \| 'auto'` | `'auto'` | The chart height in pixels, or 'auto' to calculate from aspect ratio |
62
- | `aspectRatio` | `number` | `3/2` | The desired width-to-height ratio when dimensions are auto-calculated |
63
- | `className` | `string` | `undefined` | Additional CSS classes to apply to the container |
64
-
65
- #### Sizing Options
66
-
67
- The chart can be sized in multiple ways:
68
-
69
- - **Fixed dimensions**: Provide both width and height as numbers
70
- - **Fixed width, proportional height**: Provide width as number, height as 'auto'
71
- - **Fixed height, proportional width**: Provide height as number, width as 'auto'
72
- - **Fully responsive**: Leave both as 'auto' (default), chart will fill container while maintaining aspect ratio
73
-
74
- #### Examples
75
-
76
- **Fixed size chart:**
38
+ ## Compound component API (editable chart workflow)
77
39
 
78
40
  ```tsx
79
- <VegaLiteChart
80
- width={600}
81
- height={400}
82
- sqlQuery="SELECT category, count(*) as count FROM sales GROUP BY category"
83
- spec={{
84
- mark: 'bar',
85
- encoding: {
86
- x: {field: 'category', type: 'nominal'},
87
- y: {field: 'count', type: 'quantitative'},
88
- },
89
- }}
90
- />
91
- ```
41
+ import {VegaLiteChart, type VisualizationSpec} from '@sqlrooms/vega';
92
42
 
93
- **Responsive chart with 16:9 aspect ratio:**
43
+ const initialSpec: VisualizationSpec = {
44
+ mark: 'line',
45
+ encoding: {
46
+ x: {field: 'date', type: 'temporal'},
47
+ y: {field: 'value', type: 'quantitative'},
48
+ },
49
+ };
94
50
 
95
- ```tsx
96
- <VegaLiteChart
97
- className="max-w-[600px]"
98
- aspectRatio={16 / 9}
99
- sqlQuery="SELECT date, value FROM metrics"
100
- spec={{
101
- mark: 'line',
102
- encoding: {
103
- x: {field: 'date', type: 'temporal'},
104
- y: {field: 'value', type: 'quantitative'},
105
- },
106
- }}
107
- />
51
+ export function CompoundVegaChart() {
52
+ return (
53
+ <VegaLiteChart.Container
54
+ spec={initialSpec}
55
+ sqlQuery="SELECT date, value FROM metrics"
56
+ editable
57
+ onSpecChange={(spec) => console.log('next spec', spec)}
58
+ onSqlChange={(sql) => console.log('next sql', sql)}
59
+ >
60
+ <VegaLiteChart.Actions />
61
+ <VegaLiteChart.Chart />
62
+ <VegaLiteChart.SpecEditor />
63
+ <VegaLiteChart.SqlEditor />
64
+ </VegaLiteChart.Container>
65
+ );
66
+ }
108
67
  ```
109
68
 
110
- ### VegaChartTool
111
-
112
- A tool for creating Vega-Lite charts in AI-assisted workflows, designed to work with the [SQLRooms AI assistant framework](/api/ai/).
113
-
114
- #### Features
115
-
116
- - **AI Integration**: Allows AI assistants to create data visualizations
117
- - **SQL-Powered**: Uses SQL queries to fetch data for visualizations
118
- - **Declarative Specs**: Uses Vega-Lite's declarative JSON syntax for chart creation
119
- - **Responsive Design**: Charts automatically adapt to container size
120
-
121
- #### Usage
69
+ ## AI integration (`createVegaChartTool`)
122
70
 
123
71
  ```tsx
72
+ import {
73
+ createAiSlice,
74
+ createDefaultAiInstructions,
75
+ createDefaultAiTools,
76
+ } from '@sqlrooms/ai';
124
77
  import {createVegaChartTool} from '@sqlrooms/vega';
125
- import {createAiSlice} from '@sqlrooms/ai';
126
- import {createRoomStore} from '@sqlrooms/room-shell';
127
-
128
- // Create a room store with the VegaChartTool configured
129
- export const {roomStore, useRoomStore} = createRoomStore((set, get, store) => ({
130
- // Other slices and state...
131
-
132
- // AI slice with custom tools
133
- ...createAiSlice({
134
- getApiKey: (modelProvider: string) => {
135
- return get()?.apiKeys[modelProvider] || '';
136
- },
137
- // Configure custom tools at store creation time
138
- customTools: {
139
- // Add the VegaChart tool
140
- chart: createVegaChartTool({
141
- // Optional custom description
142
- description:
143
- 'Create data visualizations using Vega-Lite and SQL queries',
144
- }),
145
-
146
- // Other custom tools...
147
- },
148
- })(set, get, store),
149
-
150
- // Other state and methods...
151
- }));
152
- ```
153
-
154
- #### Tool Parameters
155
-
156
- The VegaChartTool accepts the following parameters:
157
78
 
158
- | Parameter | Type | Description |
159
- | -------------- | -------- | ----------------------------------------------------------- |
160
- | `sqlQuery` | `string` | The SQL query to fetch data for the chart |
161
- | `vegaLiteSpec` | `string` | The Vega-Lite specification as a JSON string |
162
- | `reasoning` | `string` | Optional explanation of the chart's purpose or significance |
163
-
164
- #### Example AI Assistant Usage
165
-
166
- When the AI assistant uses the VegaChartTool, it will generate a response like this:
167
-
168
- ```json
169
- {
170
- "sqlQuery": "SELECT product_category, SUM(sales) as total_sales FROM sales GROUP BY product_category ORDER BY total_sales DESC LIMIT 10",
171
- "vegaLiteSpec": "{\"mark\": \"bar\", \"encoding\": {\"x\": {\"field\": \"product_category\", \"type\": \"nominal\", \"sort\": \"-y\"}, \"y\": {\"field\": \"total_sales\", \"type\": \"quantitative\", \"title\": \"Total Sales\"}, \"color\": {\"field\": \"product_category\", \"type\": \"nominal\", \"legend\": null}}}",
172
- "reasoning": "This chart visualizes the top 10 product categories by total sales to identify the best-performing categories."
173
- }
79
+ // inside your createRoomStore composer
80
+ createAiSlice({
81
+ tools: {
82
+ ...createDefaultAiTools(store),
83
+ chart: createVegaChartTool({
84
+ editable: true,
85
+ editorMode: 'both',
86
+ }),
87
+ },
88
+ getInstructions: () => createDefaultAiInstructions(store),
89
+ })(set, get, store);
174
90
  ```
175
91
 
176
- In a conversation with the AI assistant, it might look like this:
92
+ `createVegaChartTool` constructor options:
177
93
 
178
- ```
179
- User: Can you show me a chart of our top 10 product categories by sales?
180
-
181
- AI: I'll create a visualization of your top 10 product categories by sales.
182
-
183
- [Chart: Bar chart showing product categories on the x-axis and total sales on the y-axis]
184
-
185
- This chart displays your top 10 product categories ranked by total sales. As you can see,
186
- Electronics leads with the highest sales, followed by Furniture and Clothing. This visualization
187
- helps identify which product categories are driving the most revenue for your business.
188
- ```
94
+ - `editable`: whether users can edit SQL/spec in the chart UI
95
+ - `editorMode`: which editors to render (`'none' | 'sql' | 'vega' | 'both'`)
189
96
 
190
- The tool will:
97
+ ### LLM invocation / Zod schema fields
191
98
 
192
- 1. Execute the SQL query to fetch the data
193
- 2. Apply the Vega-Lite specification to create the visualization
194
- 3. Render the chart in the UI with responsive sizing
99
+ At runtime, the tool call payload is validated by a Zod schema.
100
+ These fields are supplied by the LLM when invoking the tool (not passed into
101
+ `createVegaChartTool(...)`):
195
102
 
196
- ## Dependencies
103
+ - `sqlQuery`: SQL used to fetch chart data
104
+ - `vegaLiteSpec`: Vega-Lite JSON string
105
+ - `reasoning`: explanation shown to users for why this chart/spec was chosen
197
106
 
198
- This package depends on:
107
+ ## Example apps
199
108
 
200
- - `@sqlrooms/duckdb` - For SQL query execution
201
- - `@sqlrooms/ui` - For UI utilities
202
- - `@sqlrooms/utils` - For utility functions
203
- - `@sqlrooms/ai` - For AI assistant integration
204
- - `react-vega` - For rendering Vega-Lite visualizations
109
+ - Vega example: https://github.com/sqlrooms/examples/tree/main/vega
110
+ - AI example (with chart tool): https://github.com/sqlrooms/examples/tree/main/ai
@@ -1 +1 @@
1
- {"version":3,"file":"VegaMonacoEditor.d.ts","sourceRoot":"","sources":["../../src/editor/VegaMonacoEditor.tsx"],"names":[],"mappings":"AACA,OAAO,EAAe,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAA0B,MAAM,OAAO,CAAC;AAG/C,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,iBAAiB,EACjB,UAAU,CACX;IACC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAwE5D,CAAC"}
1
+ {"version":3,"file":"VegaMonacoEditor.d.ts","sourceRoot":"","sources":["../../src/editor/VegaMonacoEditor.tsx"],"names":[],"mappings":"AACA,OAAO,EAAe,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAA0B,MAAM,OAAO,CAAC;AAG/C,MAAM,WAAW,qBAAsB,SAAQ,IAAI,CACjD,iBAAiB,EACjB,UAAU,CACX;IACC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAuE5D,CAAC"}
@@ -7,7 +7,6 @@ import { loadVegaLiteSchema } from '../schema/vegaLiteSchema';
7
7
  * Automatically loads and configures the Vega-Lite JSON schema for validation.
8
8
  */
9
9
  export const VegaMonacoEditor = ({ value, onChange, readOnly = false, className, enableSchemaValidation = true, onMount, options, ...props }) => {
10
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
10
  const monacoRef = useRef(null);
12
11
  const schemaConfiguredRef = useRef(false);
13
12
  // Load and configure schema
@@ -54,7 +53,6 @@ export const VegaMonacoEditor = ({ value, onChange, readOnly = false, className,
54
53
  /**
55
54
  * Configure Monaco JSON schema validation for Vega-Lite
56
55
  */
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
56
  function configureJsonSchema(monaco, schema) {
59
57
  monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
60
58
  validate: true,
@@ -1 +1 @@
1
- {"version":3,"file":"VegaMonacoEditor.js","sourceRoot":"","sources":["../../src/editor/VegaMonacoEditor.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,YAAY,EAAoB,MAAM,yBAAyB,CAAC;AACxE,OAAc,EAAC,SAAS,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAC,kBAAkB,EAAC,MAAM,0BAA0B,CAAC;AAa5D;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,KAAK,EACL,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,SAAS,EACT,sBAAsB,GAAG,IAAI,EAC7B,OAAO,EACP,OAAO,EACP,GAAG,KAAK,EACT,EAAE,EAAE;IACH,8DAA8D;IAC9D,MAAM,SAAS,GAAG,MAAM,CAAM,IAAI,CAAC,CAAC;IACpC,MAAM,mBAAmB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAE1C,4BAA4B;IAC5B,SAAS,CAAC,GAAG,EAAE;QACb,IACE,sBAAsB;YACtB,SAAS,CAAC,OAAO;YACjB,CAAC,mBAAmB,CAAC,OAAO,EAC5B,CAAC;YACD,kBAAkB,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACnC,IAAI,SAAS,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC;oBAChC,mBAAmB,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAC/C,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAE7B,MAAM,oBAAoB,GAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;QACvD,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;QAE3B,8CAA8C;QAC9C,IAAI,sBAAsB,EAAE,CAAC;YAC3B,kBAAkB,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACnC,IAAI,MAAM,EAAE,CAAC;oBACX,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACpC,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,yBAAyB;QACzB,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,SAAS,CAAC,8BAA8B,CAAC,EAAE,GAAG,EAAE,CAAC;QAC1D,CAAC,EAAE,GAAG,CAAC,CAAC;QAER,wCAAwC;QACxC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEF,OAAO,CACL,KAAC,YAAY,IACX,QAAQ,EAAC,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE;YACP,aAAa,EAAE,IAAI;YACnB,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,IAAI;YACb,eAAe,EAAE,aAAa;YAC9B,WAAW,EAAE,KAAK;YAClB,oBAAoB,EAAE,KAAK;YAC3B,GAAG,OAAO;SACX,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,8DAA8D;AAC9D,SAAS,mBAAmB,CAAC,MAAW,EAAE,MAAc;IACtD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC;QACvD,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE;YACP;gBACE,GAAG,EAAE,iDAAiD;gBACtD,SAAS,EAAE,CAAC,GAAG,CAAC;gBAChB,MAAM,EAAE,MAAiC;aAC1C;SACF;QACD,mBAAmB,EAAE,KAAK;KAC3B,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {OnMount} from '@monaco-editor/react';\nimport {MonacoEditor, MonacoEditorProps} from '@sqlrooms/monaco-editor';\nimport React, {useEffect, useRef} from 'react';\nimport {loadVegaLiteSchema} from '../schema/vegaLiteSchema';\n\nexport interface VegaMonacoEditorProps extends Omit<\n MonacoEditorProps,\n 'language'\n> {\n /**\n * Whether to enable Vega-Lite JSON schema validation\n * @default true\n */\n enableSchemaValidation?: boolean;\n}\n\n/**\n * A Monaco editor specialized for editing Vega-Lite specifications.\n * Automatically loads and configures the Vega-Lite JSON schema for validation.\n */\nexport const VegaMonacoEditor: React.FC<VegaMonacoEditorProps> = ({\n value,\n onChange,\n readOnly = false,\n className,\n enableSchemaValidation = true,\n onMount,\n options,\n ...props\n}) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const monacoRef = useRef<any>(null);\n const schemaConfiguredRef = useRef(false);\n\n // Load and configure schema\n useEffect(() => {\n if (\n enableSchemaValidation &&\n monacoRef.current &&\n !schemaConfiguredRef.current\n ) {\n loadVegaLiteSchema().then((schema) => {\n if (monacoRef.current && schema) {\n configureJsonSchema(monacoRef.current, schema);\n schemaConfiguredRef.current = true;\n }\n });\n }\n }, [enableSchemaValidation]);\n\n const handleEditorDidMount: OnMount = (editor, monaco) => {\n monacoRef.current = monaco;\n\n // Configure JSON schema validation if enabled\n if (enableSchemaValidation) {\n loadVegaLiteSchema().then((schema) => {\n if (schema) {\n configureJsonSchema(monaco, schema);\n schemaConfiguredRef.current = true;\n }\n });\n }\n\n // Format on initial load\n setTimeout(() => {\n editor.getAction('editor.action.formatDocument')?.run();\n }, 100);\n\n // Call the original onMount if provided\n onMount?.(editor, monaco);\n };\n\n return (\n <MonacoEditor\n language=\"json\"\n value={value}\n onChange={onChange}\n onMount={handleEditorDidMount}\n className={className}\n readOnly={readOnly}\n options={{\n formatOnPaste: true,\n formatOnType: true,\n folding: true,\n foldingStrategy: 'indentation',\n lineNumbers: 'off',\n fixedOverflowWidgets: false,\n ...options,\n }}\n {...props}\n />\n );\n};\n\n/**\n * Configure Monaco JSON schema validation for Vega-Lite\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction configureJsonSchema(monaco: any, schema: object) {\n monaco.languages.json.jsonDefaults.setDiagnosticsOptions({\n validate: true,\n schemas: [\n {\n uri: 'https://vega.github.io/schema/vega-lite/v5.json',\n fileMatch: ['*'],\n schema: schema as Record<string, unknown>,\n },\n ],\n enableSchemaRequest: false,\n });\n}\n"]}
1
+ {"version":3,"file":"VegaMonacoEditor.js","sourceRoot":"","sources":["../../src/editor/VegaMonacoEditor.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,YAAY,EAAoB,MAAM,yBAAyB,CAAC;AACxE,OAAc,EAAC,SAAS,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAC,kBAAkB,EAAC,MAAM,0BAA0B,CAAC;AAa5D;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,KAAK,EACL,QAAQ,EACR,QAAQ,GAAG,KAAK,EAChB,SAAS,EACT,sBAAsB,GAAG,IAAI,EAC7B,OAAO,EACP,OAAO,EACP,GAAG,KAAK,EACT,EAAE,EAAE;IACH,MAAM,SAAS,GAAG,MAAM,CAAM,IAAI,CAAC,CAAC;IACpC,MAAM,mBAAmB,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAE1C,4BAA4B;IAC5B,SAAS,CAAC,GAAG,EAAE;QACb,IACE,sBAAsB;YACtB,SAAS,CAAC,OAAO;YACjB,CAAC,mBAAmB,CAAC,OAAO,EAC5B,CAAC;YACD,kBAAkB,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACnC,IAAI,SAAS,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC;oBAChC,mBAAmB,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBAC/C,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC;IAE7B,MAAM,oBAAoB,GAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;QACvD,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;QAE3B,8CAA8C;QAC9C,IAAI,sBAAsB,EAAE,CAAC;YAC3B,kBAAkB,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACnC,IAAI,MAAM,EAAE,CAAC;oBACX,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACpC,mBAAmB,CAAC,OAAO,GAAG,IAAI,CAAC;gBACrC,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,yBAAyB;QACzB,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,SAAS,CAAC,8BAA8B,CAAC,EAAE,GAAG,EAAE,CAAC;QAC1D,CAAC,EAAE,GAAG,CAAC,CAAC;QAER,wCAAwC;QACxC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEF,OAAO,CACL,KAAC,YAAY,IACX,QAAQ,EAAC,MAAM,EACf,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE;YACP,aAAa,EAAE,IAAI;YACnB,YAAY,EAAE,IAAI;YAClB,OAAO,EAAE,IAAI;YACb,eAAe,EAAE,aAAa;YAC9B,WAAW,EAAE,KAAK;YAClB,oBAAoB,EAAE,KAAK;YAC3B,GAAG,OAAO;SACX,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,SAAS,mBAAmB,CAAC,MAAW,EAAE,MAAc;IACtD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC;QACvD,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE;YACP;gBACE,GAAG,EAAE,iDAAiD;gBACtD,SAAS,EAAE,CAAC,GAAG,CAAC;gBAChB,MAAM,EAAE,MAAiC;aAC1C;SACF;QACD,mBAAmB,EAAE,KAAK;KAC3B,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {OnMount} from '@monaco-editor/react';\nimport {MonacoEditor, MonacoEditorProps} from '@sqlrooms/monaco-editor';\nimport React, {useEffect, useRef} from 'react';\nimport {loadVegaLiteSchema} from '../schema/vegaLiteSchema';\n\nexport interface VegaMonacoEditorProps extends Omit<\n MonacoEditorProps,\n 'language'\n> {\n /**\n * Whether to enable Vega-Lite JSON schema validation\n * @default true\n */\n enableSchemaValidation?: boolean;\n}\n\n/**\n * A Monaco editor specialized for editing Vega-Lite specifications.\n * Automatically loads and configures the Vega-Lite JSON schema for validation.\n */\nexport const VegaMonacoEditor: React.FC<VegaMonacoEditorProps> = ({\n value,\n onChange,\n readOnly = false,\n className,\n enableSchemaValidation = true,\n onMount,\n options,\n ...props\n}) => {\n const monacoRef = useRef<any>(null);\n const schemaConfiguredRef = useRef(false);\n\n // Load and configure schema\n useEffect(() => {\n if (\n enableSchemaValidation &&\n monacoRef.current &&\n !schemaConfiguredRef.current\n ) {\n loadVegaLiteSchema().then((schema) => {\n if (monacoRef.current && schema) {\n configureJsonSchema(monacoRef.current, schema);\n schemaConfiguredRef.current = true;\n }\n });\n }\n }, [enableSchemaValidation]);\n\n const handleEditorDidMount: OnMount = (editor, monaco) => {\n monacoRef.current = monaco;\n\n // Configure JSON schema validation if enabled\n if (enableSchemaValidation) {\n loadVegaLiteSchema().then((schema) => {\n if (schema) {\n configureJsonSchema(monaco, schema);\n schemaConfiguredRef.current = true;\n }\n });\n }\n\n // Format on initial load\n setTimeout(() => {\n editor.getAction('editor.action.formatDocument')?.run();\n }, 100);\n\n // Call the original onMount if provided\n onMount?.(editor, monaco);\n };\n\n return (\n <MonacoEditor\n language=\"json\"\n value={value}\n onChange={onChange}\n onMount={handleEditorDidMount}\n className={className}\n readOnly={readOnly}\n options={{\n formatOnPaste: true,\n formatOnType: true,\n folding: true,\n foldingStrategy: 'indentation',\n lineNumbers: 'off',\n fixedOverflowWidgets: false,\n ...options,\n }}\n {...props}\n />\n );\n};\n\n/**\n * Configure Monaco JSON schema validation for Vega-Lite\n */\nfunction configureJsonSchema(monaco: any, schema: object) {\n monaco.languages.json.jsonDefaults.setDiagnosticsOptions({\n validate: true,\n schemas: [\n {\n uri: 'https://vega.github.io/schema/vega-lite/v5.json',\n fileMatch: ['*'],\n schema: schema as Record<string, unknown>,\n },\n ],\n enableSchemaRequest: false,\n });\n}\n"]}
@@ -24,6 +24,6 @@ export const VegaSpecEditorPanel = ({ className, title = 'Vega-Lite Spec', }) =>
24
24
  if (value !== undefined) {
25
25
  actions.setEditedSpec(value);
26
26
  }
27
- }, readOnly: !editable }) }), state.specParseError && (_jsxs("div", { className: "bg-destructive/90 text-destructive-foreground flex items-center gap-2 px-3 py-2 backdrop-blur-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 flex-shrink-0" }), _jsx("span", { className: "truncate text-xs", children: state.specParseError })] }))] }));
27
+ }, readOnly: !editable }) }), state.specParseError && (_jsxs("div", { className: "bg-destructive/90 text-destructive-foreground flex items-center gap-2 px-3 py-2 backdrop-blur-sm", children: [_jsx(AlertCircle, { className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "truncate text-xs", children: state.specParseError })] }))] }));
28
28
  };
29
29
  //# sourceMappingURL=VegaSpecEditorPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VegaSpecEditorPanel.js","sourceRoot":"","sources":["../../src/editor/VegaSpecEditorPanel.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAChC,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAcpD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAuC,CAAC,EACtE,SAAS,EACT,KAAK,GAAG,gBAAgB,GACzB,EAAE,EAAE;IACH,MAAM,EAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAC,GAAG,oBAAoB,EAAE,CAAC;IAC1D,MAAM,UAAU,GAAG,KAAK,KAAK,EAAE,CAAC;IAEhC,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,sBAAsB,EAAE,SAAS,CAAC,aAElD,UAAU,IAAI,CACb,eAAK,SAAS,EAAC,sDAAsD,aACnE,eAAM,SAAS,EAAC,qBAAqB,YAAE,KAAK,GAAQ,EACnD,KAAK,CAAC,WAAW,IAAI,CACpB,eAAM,SAAS,EAAC,+BAA+B,yBAAgB,CAChE,IACG,CACP,EAGD,cAAK,SAAS,EAAC,iBAAiB,YAC9B,KAAC,gBAAgB,IACf,SAAS,EAAC,gCAAgC,EAC1C,KAAK,EAAE,KAAK,CAAC,gBAAgB,EAC7B,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;wBAClB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;4BACxB,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;wBAC/B,CAAC;oBACH,CAAC,EACD,QAAQ,EAAE,CAAC,QAAQ,GACnB,GACE,EAGL,KAAK,CAAC,cAAc,IAAI,CACvB,eAAK,SAAS,EAAC,kGAAkG,aAC/G,KAAC,WAAW,IAAC,SAAS,EAAC,uBAAuB,GAAG,EACjD,eAAM,SAAS,EAAC,kBAAkB,YAAE,KAAK,CAAC,cAAc,GAAQ,IAC5D,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {cn} from '@sqlrooms/ui';\nimport {AlertCircle} from 'lucide-react';\nimport React from 'react';\nimport {useVegaEditorContext} from './VegaEditorContext';\nimport {VegaMonacoEditor} from './VegaMonacoEditor';\n\nexport interface VegaSpecEditorPanelProps {\n /**\n * Custom class name for the panel\n */\n className?: string;\n /**\n * Title shown in the panel header\n * @default \"Vega-Lite Spec\"\n */\n title?: string;\n}\n\n/**\n * Spec editor panel subcomponent for VegaLiteChart.Container.\n * Renders a Monaco editor with Vega-Lite JSON schema validation.\n *\n * Must be used within a VegaLiteChart.Container component.\n *\n * @example\n * ```tsx\n * <VegaLiteChart.Container spec={spec}>\n * <VegaLiteChart.Chart />\n * <VegaLiteChart.SpecEditor />\n * </VegaLiteChart.Container>\n * ```\n */\nexport const VegaSpecEditorPanel: React.FC<VegaSpecEditorPanelProps> = ({\n className,\n title = 'Vega-Lite Spec',\n}) => {\n const {state, actions, editable} = useVegaEditorContext();\n const showHeader = title !== '';\n\n return (\n <div className={cn('flex h-full flex-col', className)}>\n {/* Header - only show if title is provided */}\n {showHeader && (\n <div className=\"flex items-center justify-between border-b px-3 py-2\">\n <span className=\"text-sm font-medium\">{title}</span>\n {state.isSpecDirty && (\n <span className=\"text-muted-foreground text-xs\">Modified</span>\n )}\n </div>\n )}\n\n {/* Editor */}\n <div className=\"relative flex-1\">\n <VegaMonacoEditor\n className=\"absolute inset-0 h-full w-full\"\n value={state.editedSpecString}\n onChange={(value) => {\n if (value !== undefined) {\n actions.setEditedSpec(value);\n }\n }}\n readOnly={!editable}\n />\n </div>\n\n {/* Spec parse error */}\n {state.specParseError && (\n <div className=\"bg-destructive/90 text-destructive-foreground flex items-center gap-2 px-3 py-2 backdrop-blur-sm\">\n <AlertCircle className=\"h-4 w-4 flex-shrink-0\" />\n <span className=\"truncate text-xs\">{state.specParseError}</span>\n </div>\n )}\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"VegaSpecEditorPanel.js","sourceRoot":"","sources":["../../src/editor/VegaSpecEditorPanel.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,EAAE,EAAC,MAAM,cAAc,CAAC;AAChC,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAC,oBAAoB,EAAC,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AAcpD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAuC,CAAC,EACtE,SAAS,EACT,KAAK,GAAG,gBAAgB,GACzB,EAAE,EAAE;IACH,MAAM,EAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAC,GAAG,oBAAoB,EAAE,CAAC;IAC1D,MAAM,UAAU,GAAG,KAAK,KAAK,EAAE,CAAC;IAEhC,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAAC,sBAAsB,EAAE,SAAS,CAAC,aAElD,UAAU,IAAI,CACb,eAAK,SAAS,EAAC,sDAAsD,aACnE,eAAM,SAAS,EAAC,qBAAqB,YAAE,KAAK,GAAQ,EACnD,KAAK,CAAC,WAAW,IAAI,CACpB,eAAM,SAAS,EAAC,+BAA+B,yBAAgB,CAChE,IACG,CACP,EAGD,cAAK,SAAS,EAAC,iBAAiB,YAC9B,KAAC,gBAAgB,IACf,SAAS,EAAC,gCAAgC,EAC1C,KAAK,EAAE,KAAK,CAAC,gBAAgB,EAC7B,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;wBAClB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;4BACxB,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;wBAC/B,CAAC;oBACH,CAAC,EACD,QAAQ,EAAE,CAAC,QAAQ,GACnB,GACE,EAGL,KAAK,CAAC,cAAc,IAAI,CACvB,eAAK,SAAS,EAAC,kGAAkG,aAC/G,KAAC,WAAW,IAAC,SAAS,EAAC,kBAAkB,GAAG,EAC5C,eAAM,SAAS,EAAC,kBAAkB,YAAE,KAAK,CAAC,cAAc,GAAQ,IAC5D,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {cn} from '@sqlrooms/ui';\nimport {AlertCircle} from 'lucide-react';\nimport React from 'react';\nimport {useVegaEditorContext} from './VegaEditorContext';\nimport {VegaMonacoEditor} from './VegaMonacoEditor';\n\nexport interface VegaSpecEditorPanelProps {\n /**\n * Custom class name for the panel\n */\n className?: string;\n /**\n * Title shown in the panel header\n * @default \"Vega-Lite Spec\"\n */\n title?: string;\n}\n\n/**\n * Spec editor panel subcomponent for VegaLiteChart.Container.\n * Renders a Monaco editor with Vega-Lite JSON schema validation.\n *\n * Must be used within a VegaLiteChart.Container component.\n *\n * @example\n * ```tsx\n * <VegaLiteChart.Container spec={spec}>\n * <VegaLiteChart.Chart />\n * <VegaLiteChart.SpecEditor />\n * </VegaLiteChart.Container>\n * ```\n */\nexport const VegaSpecEditorPanel: React.FC<VegaSpecEditorPanelProps> = ({\n className,\n title = 'Vega-Lite Spec',\n}) => {\n const {state, actions, editable} = useVegaEditorContext();\n const showHeader = title !== '';\n\n return (\n <div className={cn('flex h-full flex-col', className)}>\n {/* Header - only show if title is provided */}\n {showHeader && (\n <div className=\"flex items-center justify-between border-b px-3 py-2\">\n <span className=\"text-sm font-medium\">{title}</span>\n {state.isSpecDirty && (\n <span className=\"text-muted-foreground text-xs\">Modified</span>\n )}\n </div>\n )}\n\n {/* Editor */}\n <div className=\"relative flex-1\">\n <VegaMonacoEditor\n className=\"absolute inset-0 h-full w-full\"\n value={state.editedSpecString}\n onChange={(value) => {\n if (value !== undefined) {\n actions.setEditedSpec(value);\n }\n }}\n readOnly={!editable}\n />\n </div>\n\n {/* Spec parse error */}\n {state.specParseError && (\n <div className=\"bg-destructive/90 text-destructive-foreground flex items-center gap-2 px-3 py-2 backdrop-blur-sm\">\n <AlertCircle className=\"h-4 w-4 shrink-0\" />\n <span className=\"truncate text-xs\">{state.specParseError}</span>\n </div>\n )}\n </div>\n );\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sqlrooms/vega",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/index.js",
@@ -20,12 +20,12 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@openassistant/utils": "1.0.0-alpha.0",
23
- "@sqlrooms/ai": "0.27.0",
24
- "@sqlrooms/duckdb": "0.27.0",
25
- "@sqlrooms/monaco-editor": "0.27.0",
26
- "@sqlrooms/sql-editor": "0.27.0",
27
- "@sqlrooms/ui": "0.27.0",
28
- "@sqlrooms/utils": "0.27.0",
23
+ "@sqlrooms/ai": "0.28.0",
24
+ "@sqlrooms/duckdb": "0.28.0",
25
+ "@sqlrooms/monaco-editor": "0.28.0",
26
+ "@sqlrooms/sql-editor": "0.28.0",
27
+ "@sqlrooms/ui": "0.28.0",
28
+ "@sqlrooms/utils": "0.28.0",
29
29
  "lucide-react": "^0.556.0",
30
30
  "react-vega": "^8.0.0",
31
31
  "vega": "^6.2.0",
@@ -48,5 +48,5 @@
48
48
  "typecheck": "tsc --noEmit",
49
49
  "typedoc": "typedoc"
50
50
  },
51
- "gitHead": "f215995ab4adeac4c58171739261a15cbba9e82b"
51
+ "gitHead": "dcac54f8adf77240e293c93d224a0ce9fd8142a9"
52
52
  }