@sqlrooms/sql-editor 0.14.0 → 0.16.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.
Files changed (93) hide show
  1. package/README.md +3 -4
  2. package/dist/SqlEditor.d.ts.map +1 -1
  3. package/dist/SqlEditor.js +22 -136
  4. package/dist/SqlEditor.js.map +1 -1
  5. package/dist/SqlEditorSlice.d.ts +58 -15
  6. package/dist/SqlEditorSlice.d.ts.map +1 -1
  7. package/dist/SqlEditorSlice.js +244 -94
  8. package/dist/SqlEditorSlice.js.map +1 -1
  9. package/dist/SqlMonacoEditor.d.ts.map +1 -1
  10. package/dist/SqlMonacoEditor.js +2 -1
  11. package/dist/SqlMonacoEditor.js.map +1 -1
  12. package/dist/components/CreateTableModal.d.ts.map +1 -0
  13. package/dist/{CreateTableModal.js → components/CreateTableModal.js} +2 -10
  14. package/dist/components/CreateTableModal.js.map +1 -0
  15. package/dist/components/DeleteSqlQueryModal.d.ts.map +1 -0
  16. package/dist/components/DeleteSqlQueryModal.js.map +1 -0
  17. package/dist/components/QueryEditorPanel.d.ts +9 -0
  18. package/dist/components/QueryEditorPanel.d.ts.map +1 -0
  19. package/dist/components/QueryEditorPanel.js +14 -0
  20. package/dist/components/QueryEditorPanel.js.map +1 -0
  21. package/dist/components/QueryEditorPanelActions.d.ts +5 -0
  22. package/dist/components/QueryEditorPanelActions.d.ts.map +1 -0
  23. package/dist/components/QueryEditorPanelActions.js +53 -0
  24. package/dist/components/QueryEditorPanelActions.js.map +1 -0
  25. package/dist/components/QueryEditorPanelEditor.d.ts +5 -0
  26. package/dist/components/QueryEditorPanelEditor.d.ts.map +1 -0
  27. package/dist/components/QueryEditorPanelEditor.js +45 -0
  28. package/dist/components/QueryEditorPanelEditor.js.map +1 -0
  29. package/dist/components/QueryEditorPanelTabsList.d.ts +5 -0
  30. package/dist/components/QueryEditorPanelTabsList.d.ts.map +1 -0
  31. package/dist/components/QueryEditorPanelTabsList.js +71 -0
  32. package/dist/components/QueryEditorPanelTabsList.js.map +1 -0
  33. package/dist/components/QueryResultPanel.d.ts +12 -0
  34. package/dist/components/QueryResultPanel.d.ts.map +1 -0
  35. package/dist/components/QueryResultPanel.js +30 -0
  36. package/dist/components/QueryResultPanel.js.map +1 -0
  37. package/dist/components/RenameSqlQueryModal.d.ts.map +1 -0
  38. package/dist/components/RenameSqlQueryModal.js +29 -0
  39. package/dist/components/RenameSqlQueryModal.js.map +1 -0
  40. package/dist/components/SqlEditorHeader.d.ts +15 -0
  41. package/dist/components/SqlEditorHeader.d.ts.map +1 -0
  42. package/dist/components/SqlEditorHeader.js +7 -0
  43. package/dist/components/SqlEditorHeader.js.map +1 -0
  44. package/dist/components/SqlQueryDataSourcesPanel.d.ts.map +1 -0
  45. package/dist/components/SqlQueryDataSourcesPanel.js.map +1 -0
  46. package/dist/components/SqlReferenceButton.d.ts +12 -0
  47. package/dist/components/SqlReferenceButton.d.ts.map +1 -0
  48. package/dist/components/SqlReferenceButton.js +10 -0
  49. package/dist/components/SqlReferenceButton.js.map +1 -0
  50. package/dist/components/TableStructurePanel.d.ts +13 -0
  51. package/dist/components/TableStructurePanel.d.ts.map +1 -0
  52. package/dist/components/TableStructurePanel.js +21 -0
  53. package/dist/components/TableStructurePanel.js.map +1 -0
  54. package/dist/index.d.ts +10 -2
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +10 -2
  57. package/dist/index.js.map +1 -1
  58. package/package.json +17 -16
  59. package/dist/CreateTableModal.d.ts.map +0 -1
  60. package/dist/CreateTableModal.js.map +0 -1
  61. package/dist/DeleteSqlQueryModal.d.ts.map +0 -1
  62. package/dist/DeleteSqlQueryModal.js.map +0 -1
  63. package/dist/RenameSqlQueryModal.d.ts.map +0 -1
  64. package/dist/RenameSqlQueryModal.js +0 -27
  65. package/dist/RenameSqlQueryModal.js.map +0 -1
  66. package/dist/SqlQueryDataSourcesPanel.d.ts.map +0 -1
  67. package/dist/SqlQueryDataSourcesPanel.js.map +0 -1
  68. package/dist/hooks/index.d.ts +0 -5
  69. package/dist/hooks/index.d.ts.map +0 -1
  70. package/dist/hooks/index.js +0 -5
  71. package/dist/hooks/index.js.map +0 -1
  72. package/dist/hooks/useMonacoEditor.d.ts +0 -13
  73. package/dist/hooks/useMonacoEditor.d.ts.map +0 -1
  74. package/dist/hooks/useMonacoEditor.js +0 -78
  75. package/dist/hooks/useMonacoEditor.js.map +0 -1
  76. package/dist/hooks/useQueryExecution.d.ts +0 -17
  77. package/dist/hooks/useQueryExecution.d.ts.map +0 -1
  78. package/dist/hooks/useQueryExecution.js +0 -61
  79. package/dist/hooks/useQueryExecution.js.map +0 -1
  80. package/dist/hooks/useQueryTabManagement.d.ts +0 -41
  81. package/dist/hooks/useQueryTabManagement.d.ts.map +0 -1
  82. package/dist/hooks/useQueryTabManagement.js +0 -95
  83. package/dist/hooks/useQueryTabManagement.js.map +0 -1
  84. package/dist/hooks/useTableManagement.d.ts +0 -14
  85. package/dist/hooks/useTableManagement.d.ts.map +0 -1
  86. package/dist/hooks/useTableManagement.js +0 -46
  87. package/dist/hooks/useTableManagement.js.map +0 -1
  88. /package/dist/{CreateTableModal.d.ts → components/CreateTableModal.d.ts} +0 -0
  89. /package/dist/{DeleteSqlQueryModal.d.ts → components/DeleteSqlQueryModal.d.ts} +0 -0
  90. /package/dist/{DeleteSqlQueryModal.js → components/DeleteSqlQueryModal.js} +0 -0
  91. /package/dist/{RenameSqlQueryModal.d.ts → components/RenameSqlQueryModal.d.ts} +0 -0
  92. /package/dist/{SqlQueryDataSourcesPanel.d.ts → components/SqlQueryDataSourcesPanel.d.ts} +0 -0
  93. /package/dist/{SqlQueryDataSourcesPanel.js → components/SqlQueryDataSourcesPanel.js} +0 -0
@@ -1,10 +1,11 @@
1
- import { DuckQueryError } from '@sqlrooms/duckdb';
1
+ import { getSqlErrorWithPointer, splitSqlStatements, makeLimitQuery, } from '@sqlrooms/duckdb';
2
2
  import { createSlice, useBaseProjectBuilderStore, } from '@sqlrooms/project-builder';
3
3
  import { generateUniqueName, genRandomStr } from '@sqlrooms/utils';
4
4
  import { csvFormat } from 'd3-dsv';
5
5
  import { saveAs } from 'file-saver';
6
6
  import { produce } from 'immer';
7
7
  import { z } from 'zod';
8
+ // Saved state (persisted)
8
9
  export const SqlEditorSliceConfig = z.object({
9
10
  sqlEditor: z.object({
10
11
  queries: z.array(z.object({
@@ -16,6 +17,7 @@ export const SqlEditorSliceConfig = z.object({
16
17
  .string()
17
18
  .default('default')
18
19
  .describe('The id of the currently selected query.'),
20
+ lastExecutedQuery: z.string().optional().describe('Last executed query'),
19
21
  }),
20
22
  });
21
23
  export function createDefaultSqlEditorConfig() {
@@ -26,104 +28,252 @@ export function createDefaultSqlEditorConfig() {
26
28
  },
27
29
  };
28
30
  }
29
- export function createSqlEditorSlice() {
30
- return createSlice((set, get) => ({
31
- sqlEditor: {
32
- executeQuery: async (query, schema = 'main') => {
33
- const connector = await get().db.getConnector();
34
- try {
35
- await connector.query(`SET search_path = ${schema}`);
36
- const results = await connector.query(query);
37
- await connector.query(`SET search_path = main`);
38
- // Refresh table schemas after query execution
39
- await get().db.refreshTableSchemas();
40
- return { results };
41
- }
42
- catch (e) {
43
- console.error(e);
44
- const errorMessage = e instanceof DuckQueryError
45
- ? e.getMessageForUser()
46
- : 'Query failed';
47
- return { error: errorMessage || String(e) };
48
- }
49
- },
50
- exportResultsToCsv: (results, filename) => {
51
- if (!results)
52
- return;
53
- const blob = new Blob([csvFormat(results.toArray())], {
54
- type: 'text/plain;charset=utf-8',
55
- });
56
- saveAs(blob, filename || `export-${genRandomStr(5)}.csv`);
57
- },
58
- createQueryTab: (initialQuery = '') => {
59
- const sqlEditorConfig = get().config.sqlEditor;
60
- const newQuery = {
61
- id: genRandomStr(8),
62
- name: generateUniqueName('Untitled', sqlEditorConfig.queries.map((q) => q.name)),
63
- query: initialQuery,
64
- };
65
- set((state) => produce(state, (draft) => {
66
- draft.config.sqlEditor.queries.push(newQuery);
67
- draft.config.sqlEditor.selectedQueryId = newQuery.id;
68
- }));
69
- return newQuery;
70
- },
71
- deleteQueryTab: (queryId) => {
72
- const sqlEditorConfig = get().config.sqlEditor;
73
- const queries = sqlEditorConfig.queries;
74
- if (queries.length <= 1) {
75
- // Don't delete the last query
76
- return;
77
- }
78
- const index = queries.findIndex((q) => q.id === queryId);
79
- if (index === -1)
80
- return;
81
- const isSelected = sqlEditorConfig.selectedQueryId === queryId;
82
- const filteredQueries = queries.filter((q) => q.id !== queryId);
83
- set((state) => produce(state, (draft) => {
84
- draft.config.sqlEditor.queries = filteredQueries;
85
- // If we're deleting the selected tab, select the previous one or the first one
86
- if (isSelected && filteredQueries.length > 0) {
87
- const newSelectedIndex = Math.max(0, index - 1);
88
- // Safely access the ID with fallback to the first query if needed
89
- const newSelectedId = filteredQueries[newSelectedIndex]?.id ?? filteredQueries[0]?.id;
90
- if (newSelectedId) {
91
- draft.config.sqlEditor.selectedQueryId = newSelectedId;
31
+ export function isQueryWithResult(queryResult) {
32
+ return (queryResult?.status === 'success' &&
33
+ (queryResult.type === 'pragma' ||
34
+ queryResult.type === 'explain' ||
35
+ queryResult.type === 'select'));
36
+ }
37
+ export function createSqlEditorSlice({ queryResultLimit = 100, } = {}) {
38
+ return createSlice((set, get) => {
39
+ return {
40
+ sqlEditor: {
41
+ // Initialize runtime state
42
+ isTablesLoading: false,
43
+ queryResultLimit,
44
+ exportResultsToCsv: (results, filename) => {
45
+ if (!results)
46
+ return;
47
+ const blob = new Blob([csvFormat(results.toArray())], {
48
+ type: 'text/plain;charset=utf-8',
49
+ });
50
+ saveAs(blob, filename || `export-${genRandomStr(5)}.csv`);
51
+ },
52
+ createQueryTab: (initialQuery = '') => {
53
+ const sqlEditorConfig = get().config.sqlEditor;
54
+ const newQuery = {
55
+ id: genRandomStr(8),
56
+ name: generateUniqueName('Untitled', sqlEditorConfig.queries.map((q) => q.name)),
57
+ query: initialQuery,
58
+ };
59
+ set((state) => produce(state, (draft) => {
60
+ draft.config.sqlEditor.queries.push(newQuery);
61
+ draft.config.sqlEditor.selectedQueryId = newQuery.id;
62
+ }));
63
+ return newQuery;
64
+ },
65
+ deleteQueryTab: (queryId) => {
66
+ const sqlEditorConfig = get().config.sqlEditor;
67
+ const queries = sqlEditorConfig.queries;
68
+ if (queries.length <= 1) {
69
+ // Don't delete the last query
70
+ return;
71
+ }
72
+ const index = queries.findIndex((q) => q.id === queryId);
73
+ if (index === -1)
74
+ return;
75
+ const isSelected = sqlEditorConfig.selectedQueryId === queryId;
76
+ const filteredQueries = queries.filter((q) => q.id !== queryId);
77
+ set((state) => produce(state, (draft) => {
78
+ draft.config.sqlEditor.queries = filteredQueries;
79
+ // If we're deleting the selected tab, select the previous one or the first one
80
+ if (isSelected && filteredQueries.length > 0) {
81
+ const newSelectedIndex = Math.max(0, index - 1);
82
+ // Safely access the ID with fallback to the first query if needed
83
+ const newSelectedId = filteredQueries[newSelectedIndex]?.id ??
84
+ filteredQueries[0]?.id;
85
+ if (newSelectedId) {
86
+ draft.config.sqlEditor.selectedQueryId = newSelectedId;
87
+ }
88
+ }
89
+ }));
90
+ },
91
+ renameQueryTab: (queryId, newName) => {
92
+ set((state) => produce(state, (draft) => {
93
+ const query = draft.config.sqlEditor.queries.find((q) => q.id === queryId);
94
+ if (query) {
95
+ query.name = newName || query.name;
92
96
  }
97
+ }));
98
+ },
99
+ updateQueryText: (queryId, queryText) => {
100
+ set((state) => produce(state, (draft) => {
101
+ const query = draft.config.sqlEditor.queries.find((q) => q.id === queryId);
102
+ if (query) {
103
+ query.query = queryText;
104
+ }
105
+ }));
106
+ },
107
+ setSelectedQueryId: (queryId) => {
108
+ set((state) => produce(state, (draft) => {
109
+ draft.config.sqlEditor.selectedQueryId = queryId;
110
+ }));
111
+ },
112
+ getCurrentQuery: () => {
113
+ const sqlEditorConfig = get().config.sqlEditor;
114
+ const selectedId = sqlEditorConfig.selectedQueryId;
115
+ const query = sqlEditorConfig.queries.find((q) => q.id === selectedId);
116
+ return query?.query || '';
117
+ },
118
+ /** @deprecated */
119
+ selectTable: (table) => {
120
+ set((state) => produce(state, (draft) => {
121
+ draft.sqlEditor.selectedTable = table;
122
+ }));
123
+ },
124
+ clearQueryResults: () => {
125
+ // Update state without using Immer for the Table type
126
+ set((state) => ({
127
+ ...state,
128
+ sqlEditor: {
129
+ ...state.sqlEditor,
130
+ queryResults: null,
131
+ queryError: undefined,
132
+ },
133
+ }));
134
+ },
135
+ parseAndRunCurrentQuery: async () => get().sqlEditor.parseAndRunQuery(get().sqlEditor.getCurrentQuery()),
136
+ abortCurrentQuery: () => {
137
+ const currentResult = get().sqlEditor.queryResult;
138
+ if (currentResult?.status === 'loading' && currentResult.controller) {
139
+ currentResult.controller.abort();
93
140
  }
94
- }));
95
- },
96
- renameQueryTab: (queryId, newName) => {
97
- set((state) => produce(state, (draft) => {
98
- const query = draft.config.sqlEditor.queries.find((q) => q.id === queryId);
99
- if (query) {
100
- query.name = newName || query.name;
141
+ set((state) => produce(state, (draft) => {
142
+ if (draft.sqlEditor.queryResult?.status === 'loading') {
143
+ draft.sqlEditor.queryResult.isBeingAborted = true;
144
+ }
145
+ }));
146
+ },
147
+ setQueryResultLimit: (limit) => {
148
+ set((state) => produce(state, (draft) => {
149
+ draft.sqlEditor.queryResultLimit = limit;
150
+ }));
151
+ },
152
+ parseAndRunQuery: async (query) => {
153
+ if (get().sqlEditor.queryResult?.status === 'loading') {
154
+ throw new Error('Query already running');
101
155
  }
102
- }));
103
- },
104
- updateQueryText: (queryId, queryText) => {
105
- set((state) => produce(state, (draft) => {
106
- const query = draft.config.sqlEditor.queries.find((q) => q.id === queryId);
107
- if (query) {
108
- query.query = queryText;
156
+ if (!query.trim()) {
157
+ return;
109
158
  }
110
- }));
111
- },
112
- setSelectedQueryId: (queryId) => {
113
- set((state) => produce(state, (draft) => {
114
- draft.config.sqlEditor.selectedQueryId = queryId;
115
- }));
116
- },
117
- getCurrentQuery: (defaultQuery = '') => {
118
- const sqlEditorConfig = get().config.sqlEditor;
119
- const selectedId = sqlEditorConfig.selectedQueryId;
120
- // Find query by ID
121
- const query = sqlEditorConfig.queries.find((q) => q.id === selectedId);
122
- // If found, return its query text, otherwise default
123
- return query?.query || defaultQuery;
159
+ // Create abort controller for this query execution
160
+ const queryController = new AbortController();
161
+ // First update loading state and clear results
162
+ set((state) => produce(state, (draft) => {
163
+ draft.sqlEditor.selectedTable = undefined;
164
+ draft.sqlEditor.queryResult = {
165
+ status: 'loading',
166
+ isBeingAborted: false,
167
+ controller: queryController,
168
+ };
169
+ draft.config.sqlEditor.lastExecutedQuery = query;
170
+ }));
171
+ let queryResult;
172
+ try {
173
+ const connector = await get().db.getConnector();
174
+ const signal = queryController.signal;
175
+ const statements = splitSqlStatements(query);
176
+ const allButLastStatements = statements.slice(0, -1);
177
+ const lastQueryStatement = statements[statements.length - 1];
178
+ if (!statements?.length) {
179
+ throw new Error('Empty query');
180
+ }
181
+ // Execute all but the last statements with cancellation support
182
+ for (const statement of allButLastStatements) {
183
+ if (signal.aborted) {
184
+ throw new Error('Query aborted');
185
+ }
186
+ await connector.query(statement, { signal }).result;
187
+ }
188
+ if (signal.aborted) {
189
+ throw new Error('Query aborted');
190
+ }
191
+ const parsedLastStatement = await get().db.sqlSelectToJson(lastQueryStatement);
192
+ if (signal.aborted) {
193
+ throw new Error('Query aborted');
194
+ }
195
+ const isValidSelectQuery = !parsedLastStatement.error;
196
+ if (isValidSelectQuery) {
197
+ const result = await connector.query(makeLimitQuery(lastQueryStatement, {
198
+ sanitize: false, // should already be sanitized
199
+ limit: get().sqlEditor.queryResultLimit,
200
+ }), { signal }).result;
201
+ queryResult = {
202
+ status: 'success',
203
+ type: 'select',
204
+ lastQueryStatement,
205
+ result,
206
+ };
207
+ }
208
+ else {
209
+ if (parsedLastStatement.error &&
210
+ parsedLastStatement.error_type !== 'not implemented') {
211
+ throw (`${parsedLastStatement.error_type} ${parsedLastStatement.error_subtype}: ${parsedLastStatement.error_message}` +
212
+ `\n${getSqlErrorWithPointer(lastQueryStatement, Number(parsedLastStatement.position)).formatted}`);
213
+ }
214
+ const result = await connector.query(lastQueryStatement, { signal })
215
+ .result;
216
+ // EXPLAIN and PRAGMA are not detected as select queries
217
+ // and we cannot wrap them in a SELECT * FROM,
218
+ // but we can still execute them and return the result
219
+ if (/^(EXPLAIN)/i.test(lastQueryStatement)) {
220
+ queryResult = {
221
+ status: 'success',
222
+ type: 'explain',
223
+ lastQueryStatement,
224
+ result,
225
+ };
226
+ }
227
+ else if (/^(PRAGMA)/i.test(lastQueryStatement)) {
228
+ queryResult = {
229
+ status: 'success',
230
+ type: 'pragma',
231
+ lastQueryStatement,
232
+ result,
233
+ };
234
+ }
235
+ else {
236
+ queryResult = {
237
+ status: 'success',
238
+ type: 'exec',
239
+ lastQueryStatement,
240
+ };
241
+ }
242
+ }
243
+ if (signal.aborted) {
244
+ throw new Error('Query aborted');
245
+ }
246
+ // Refresh table schemas if there are multiple statements or if the
247
+ // last statement is not a select query
248
+ if (statements.length > 1 || !isValidSelectQuery) {
249
+ get().db.refreshTableSchemas();
250
+ }
251
+ if (signal.aborted) {
252
+ throw new Error('Query aborted');
253
+ }
254
+ }
255
+ catch (e) {
256
+ console.error(e);
257
+ const errorMessage = e instanceof Error ? e.message : String(e);
258
+ if (errorMessage === 'Query aborted' ||
259
+ queryController.signal.aborted) {
260
+ queryResult = { status: 'aborted' };
261
+ }
262
+ else {
263
+ queryResult = {
264
+ status: 'error',
265
+ error: errorMessage,
266
+ };
267
+ }
268
+ }
269
+ set((state) => ({
270
+ ...state,
271
+ sqlEditor: { ...state.sqlEditor, queryResult },
272
+ }));
273
+ },
124
274
  },
125
- },
126
- }));
275
+ };
276
+ });
127
277
  }
128
278
  export function useStoreWithSqlEditor(selector) {
129
279
  return useBaseProjectBuilderStore((state) => selector(state));
@@ -1 +1 @@
1
- {"version":3,"file":"SqlEditorSlice.js","sourceRoot":"","sources":["../src/SqlEditorSlice.tsx"],"names":[],"mappings":"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","sourcesContent":["import {DuckDbSliceConfig, DuckQueryError} from '@sqlrooms/duckdb';\nimport {\n createSlice,\n ProjectBuilderState,\n StateCreator,\n useBaseProjectBuilderStore,\n BaseProjectConfig,\n} from '@sqlrooms/project-builder';\nimport {generateUniqueName, genRandomStr} from '@sqlrooms/utils';\nimport {Table} from 'apache-arrow';\nimport {csvFormat} from 'd3-dsv';\nimport {saveAs} from 'file-saver';\nimport {produce} from 'immer';\nimport {z} from 'zod';\n\nexport const SqlEditorSliceConfig = z.object({\n sqlEditor: z.object({\n queries: z.array(\n z.object({\n id: z.string().describe('Query identifier.'),\n name: z.string().describe('Query name.'),\n query: z.string().describe('SQL query to execute.'),\n }),\n ),\n selectedQueryId: z\n .string()\n .default('default')\n .describe('The id of the currently selected query.'),\n }),\n});\nexport type SqlEditorSliceConfig = z.infer<typeof SqlEditorSliceConfig>;\n\nexport function createDefaultSqlEditorConfig(): SqlEditorSliceConfig {\n return {\n sqlEditor: {\n queries: [{id: 'default', name: 'Untitled', query: ''}],\n selectedQueryId: 'default',\n },\n };\n}\n\nexport type SqlEditorSliceState = {\n sqlEditor: {\n /**\n * Execute a SQL query and return the results.\n * @param query - The SQL query to execute.\n * @param schema - The schema to use (default: main).\n */\n executeQuery(\n query: string,\n schema?: string,\n ): Promise<{\n results?: Table;\n error?: string;\n }>;\n\n /**\n * Export query results to CSV.\n * @param results - The query results to export.\n * @param filename - Optional filename (default is generated).\n */\n exportResultsToCsv(results: Table, filename?: string): void;\n\n /**\n * Create a new query tab.\n * @param initialQuery - Optional initial query text.\n */\n createQueryTab(initialQuery?: string): {\n id: string;\n name: string;\n query: string;\n };\n\n /**\n * Delete a query tab.\n * @param queryId - The ID of the query to delete.\n */\n deleteQueryTab(queryId: string): void;\n\n /**\n * Rename a query tab.\n * @param queryId - The ID of the query to rename.\n * @param newName - The new name for the query.\n */\n renameQueryTab(queryId: string, newName: string): void;\n\n /**\n * Update the SQL text for a query.\n * @param queryId - The ID of the query to update.\n * @param queryText - The new SQL text.\n */\n updateQueryText(queryId: string, queryText: string): void;\n\n /**\n * Set the selected query tab.\n * @param queryId - The ID of the query to select.\n */\n setSelectedQueryId(queryId: string): void;\n\n /**\n * Get the currently selected query's SQL text.\n * @param defaultQuery - Optional default query text to return if no query is found.\n */\n getCurrentQuery(defaultQuery?: string): string;\n };\n};\n\nexport function createSqlEditorSlice<\n PC extends BaseProjectConfig & DuckDbSliceConfig & SqlEditorSliceConfig,\n>(): StateCreator<SqlEditorSliceState> {\n return createSlice<PC, SqlEditorSliceState>((set, get) => ({\n sqlEditor: {\n executeQuery: async (query, schema = 'main') => {\n const connector = await get().db.getConnector();\n\n try {\n await connector.query(`SET search_path = ${schema}`);\n const results = await connector.query(query);\n await connector.query(`SET search_path = main`);\n\n // Refresh table schemas after query execution\n await get().db.refreshTableSchemas();\n\n return {results};\n } catch (e) {\n console.error(e);\n const errorMessage =\n e instanceof DuckQueryError\n ? e.getMessageForUser()\n : 'Query failed';\n\n return {error: errorMessage || String(e)};\n }\n },\n\n exportResultsToCsv: (results, filename) => {\n if (!results) return;\n const blob = new Blob([csvFormat(results.toArray())], {\n type: 'text/plain;charset=utf-8',\n });\n saveAs(blob, filename || `export-${genRandomStr(5)}.csv`);\n },\n\n createQueryTab: (initialQuery = '') => {\n const sqlEditorConfig = get().config.sqlEditor;\n const newQuery = {\n id: genRandomStr(8),\n name: generateUniqueName(\n 'Untitled',\n sqlEditorConfig.queries.map((q) => q.name),\n ),\n query: initialQuery,\n };\n\n set((state) =>\n produce(state, (draft) => {\n draft.config.sqlEditor.queries.push(newQuery);\n draft.config.sqlEditor.selectedQueryId = newQuery.id;\n }),\n );\n\n return newQuery;\n },\n\n deleteQueryTab: (queryId) => {\n const sqlEditorConfig = get().config.sqlEditor;\n const queries = sqlEditorConfig.queries;\n\n if (queries.length <= 1) {\n // Don't delete the last query\n return;\n }\n\n const index = queries.findIndex((q) => q.id === queryId);\n if (index === -1) return;\n\n const isSelected = sqlEditorConfig.selectedQueryId === queryId;\n const filteredQueries = queries.filter((q) => q.id !== queryId);\n\n set((state) =>\n produce(state, (draft) => {\n draft.config.sqlEditor.queries = filteredQueries;\n\n // If we're deleting the selected tab, select the previous one or the first one\n if (isSelected && filteredQueries.length > 0) {\n const newSelectedIndex = Math.max(0, index - 1);\n // Safely access the ID with fallback to the first query if needed\n const newSelectedId =\n filteredQueries[newSelectedIndex]?.id ?? filteredQueries[0]?.id;\n if (newSelectedId) {\n draft.config.sqlEditor.selectedQueryId = newSelectedId;\n }\n }\n }),\n );\n },\n\n renameQueryTab: (queryId, newName) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.config.sqlEditor.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.name = newName || query.name;\n }\n }),\n );\n },\n\n updateQueryText: (queryId, queryText) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.config.sqlEditor.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.query = queryText;\n }\n }),\n );\n },\n\n setSelectedQueryId: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n draft.config.sqlEditor.selectedQueryId = queryId;\n }),\n );\n },\n\n getCurrentQuery: (defaultQuery = '') => {\n const sqlEditorConfig = get().config.sqlEditor;\n const selectedId = sqlEditorConfig.selectedQueryId;\n // Find query by ID\n const query = sqlEditorConfig.queries.find((q) => q.id === selectedId);\n // If found, return its query text, otherwise default\n return query?.query || defaultQuery;\n },\n },\n }));\n}\n\ntype ProjectConfigWithSqlEditor = BaseProjectConfig & SqlEditorSliceConfig;\ntype ProjectStateWithSqlEditor =\n ProjectBuilderState<ProjectConfigWithSqlEditor> & SqlEditorSliceState;\n\nexport function useStoreWithSqlEditor<T>(\n selector: (state: ProjectStateWithSqlEditor) => T,\n): T {\n return useBaseProjectBuilderStore<\n BaseProjectConfig & SqlEditorSliceConfig,\n ProjectBuilderState<ProjectConfigWithSqlEditor>,\n T\n >((state) => selector(state as unknown as ProjectStateWithSqlEditor));\n}\n"]}
1
+ {"version":3,"file":"SqlEditorSlice.js","sourceRoot":"","sources":["../src/SqlEditorSlice.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n DuckDbSliceConfig,\n getSqlErrorWithPointer,\n splitSqlStatements,\n makeLimitQuery,\n} from '@sqlrooms/duckdb';\nimport {\n BaseProjectConfig,\n createSlice,\n ProjectBuilderState,\n StateCreator,\n useBaseProjectBuilderStore,\n} from '@sqlrooms/project-builder';\nimport {generateUniqueName, genRandomStr} from '@sqlrooms/utils';\nimport * as arrow from 'apache-arrow';\nimport {csvFormat} from 'd3-dsv';\nimport {saveAs} from 'file-saver';\nimport {produce} from 'immer';\nimport {z} from 'zod';\n\n// Saved state (persisted)\nexport const SqlEditorSliceConfig = z.object({\n sqlEditor: z.object({\n queries: z.array(\n z.object({\n id: z.string().describe('Query identifier.'),\n name: z.string().describe('Query name.'),\n query: z.string().describe('SQL query to execute.'),\n }),\n ),\n selectedQueryId: z\n .string()\n .default('default')\n .describe('The id of the currently selected query.'),\n lastExecutedQuery: z.string().optional().describe('Last executed query'),\n }),\n});\nexport type SqlEditorSliceConfig = z.infer<typeof SqlEditorSliceConfig>;\n\nexport function createDefaultSqlEditorConfig(): SqlEditorSliceConfig {\n return {\n sqlEditor: {\n queries: [{id: 'default', name: 'Untitled', query: ''}],\n selectedQueryId: 'default',\n },\n };\n}\n\nexport type QueryResult =\n | {status: 'loading'; isBeingAborted?: boolean; controller: AbortController}\n | {status: 'aborted'}\n | {status: 'error'; error: string}\n | {\n status: 'success';\n type: 'pragma' | 'explain' | 'select';\n result: arrow.Table | undefined;\n lastQueryStatement: string;\n }\n | {\n status: 'success';\n type: 'exec';\n lastQueryStatement: string;\n };\n\nexport function isQueryWithResult(\n queryResult: QueryResult | undefined,\n): queryResult is QueryResult & {\n status: 'success';\n type: 'pragma' | 'explain' | 'select';\n} {\n return (\n queryResult?.status === 'success' &&\n (queryResult.type === 'pragma' ||\n queryResult.type === 'explain' ||\n queryResult.type === 'select')\n );\n}\n\nexport type SqlEditorSliceState = {\n sqlEditor: {\n // Runtime state\n queryResult?: QueryResult;\n /** @deprecated */\n selectedTable?: string;\n /** @deprecated Use `useStoreWithSqlEditor((s) => s.db.isRefreshingTableSchemas)` instead. */\n isTablesLoading: boolean;\n /** @deprecated */\n tablesError?: string;\n\n queryResultLimit: number;\n\n /**\n * Run the currently selected query.\n */\n parseAndRunQuery(query: string): Promise<void>;\n\n /**\n * Run the currently selected query.\n */\n parseAndRunCurrentQuery(): Promise<void>;\n\n /**\n * Abort the currently running query.\n */\n abortCurrentQuery(): void;\n\n /**\n * Export query results to CSV.\n * @deprecated Use `useExportToCsv` from `@sqlrooms/duckdb` instead.\n */\n exportResultsToCsv(results: arrow.Table, filename?: string): void;\n\n /**\n * Create a new query tab.\n * @param initialQuery - Optional initial query text.\n */\n createQueryTab(initialQuery?: string): {\n id: string;\n name: string;\n query: string;\n };\n\n /**\n * Delete a query tab.\n * @param queryId - The ID of the query to delete.\n */\n deleteQueryTab(queryId: string): void;\n\n /**\n * Rename a query tab.\n * @param queryId - The ID of the query to rename.\n * @param newName - The new name for the query.\n */\n renameQueryTab(queryId: string, newName: string): void;\n\n /**\n * Update the SQL text for a query.\n * @param queryId - The ID of the query to update.\n * @param queryText - The new SQL text.\n */\n updateQueryText(queryId: string, queryText: string): void;\n\n /**\n * Set the selected query tab.\n * @param queryId - The ID of the query to select.\n */\n setSelectedQueryId(queryId: string): void;\n\n /**\n * Get the currently selected query's SQL text.\n */\n getCurrentQuery(): string;\n\n /** @deprecated */\n selectTable(table: string | undefined): void;\n\n clearQueryResults(): void;\n\n setQueryResultLimit(limit: number): void;\n };\n};\n\nexport function createSqlEditorSlice<\n PC extends BaseProjectConfig & DuckDbSliceConfig & SqlEditorSliceConfig,\n>({\n queryResultLimit = 100,\n}: {\n queryResultLimit?: number;\n} = {}): StateCreator<SqlEditorSliceState> {\n return createSlice<PC, SqlEditorSliceState>((set, get) => {\n return {\n sqlEditor: {\n // Initialize runtime state\n isTablesLoading: false,\n queryResultLimit,\n\n exportResultsToCsv: (results, filename) => {\n if (!results) return;\n const blob = new Blob([csvFormat(results.toArray())], {\n type: 'text/plain;charset=utf-8',\n });\n saveAs(blob, filename || `export-${genRandomStr(5)}.csv`);\n },\n\n createQueryTab: (initialQuery = '') => {\n const sqlEditorConfig = get().config.sqlEditor;\n const newQuery = {\n id: genRandomStr(8),\n name: generateUniqueName(\n 'Untitled',\n sqlEditorConfig.queries.map((q) => q.name),\n ),\n query: initialQuery,\n };\n\n set((state) =>\n produce(state, (draft) => {\n draft.config.sqlEditor.queries.push(newQuery);\n draft.config.sqlEditor.selectedQueryId = newQuery.id;\n }),\n );\n\n return newQuery;\n },\n\n deleteQueryTab: (queryId) => {\n const sqlEditorConfig = get().config.sqlEditor;\n const queries = sqlEditorConfig.queries;\n\n if (queries.length <= 1) {\n // Don't delete the last query\n return;\n }\n\n const index = queries.findIndex((q) => q.id === queryId);\n if (index === -1) return;\n\n const isSelected = sqlEditorConfig.selectedQueryId === queryId;\n const filteredQueries = queries.filter((q) => q.id !== queryId);\n\n set((state) =>\n produce(state, (draft) => {\n draft.config.sqlEditor.queries = filteredQueries;\n\n // If we're deleting the selected tab, select the previous one or the first one\n if (isSelected && filteredQueries.length > 0) {\n const newSelectedIndex = Math.max(0, index - 1);\n // Safely access the ID with fallback to the first query if needed\n const newSelectedId =\n filteredQueries[newSelectedIndex]?.id ??\n filteredQueries[0]?.id;\n if (newSelectedId) {\n draft.config.sqlEditor.selectedQueryId = newSelectedId;\n }\n }\n }),\n );\n },\n\n renameQueryTab: (queryId, newName) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.config.sqlEditor.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.name = newName || query.name;\n }\n }),\n );\n },\n\n updateQueryText: (queryId, queryText) => {\n set((state) =>\n produce(state, (draft) => {\n const query = draft.config.sqlEditor.queries.find(\n (q) => q.id === queryId,\n );\n if (query) {\n query.query = queryText;\n }\n }),\n );\n },\n\n setSelectedQueryId: (queryId) => {\n set((state) =>\n produce(state, (draft) => {\n draft.config.sqlEditor.selectedQueryId = queryId;\n }),\n );\n },\n\n getCurrentQuery: () => {\n const sqlEditorConfig = get().config.sqlEditor;\n const selectedId = sqlEditorConfig.selectedQueryId;\n const query = sqlEditorConfig.queries.find(\n (q) => q.id === selectedId,\n );\n return query?.query || '';\n },\n\n /** @deprecated */\n selectTable: (table) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.selectedTable = table;\n }),\n );\n },\n\n clearQueryResults: () => {\n // Update state without using Immer for the Table type\n set((state) => ({\n ...state,\n sqlEditor: {\n ...state.sqlEditor,\n queryResults: null,\n queryError: undefined,\n },\n }));\n },\n\n parseAndRunCurrentQuery: async (): Promise<void> =>\n get().sqlEditor.parseAndRunQuery(get().sqlEditor.getCurrentQuery()),\n\n abortCurrentQuery: () => {\n const currentResult = get().sqlEditor.queryResult;\n if (currentResult?.status === 'loading' && currentResult.controller) {\n currentResult.controller.abort();\n }\n\n set((state) =>\n produce(state, (draft) => {\n if (draft.sqlEditor.queryResult?.status === 'loading') {\n draft.sqlEditor.queryResult.isBeingAborted = true;\n }\n }),\n );\n },\n\n setQueryResultLimit: (limit) => {\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.queryResultLimit = limit;\n }),\n );\n },\n\n parseAndRunQuery: async (query): Promise<void> => {\n if (get().sqlEditor.queryResult?.status === 'loading') {\n throw new Error('Query already running');\n }\n if (!query.trim()) {\n return;\n }\n\n // Create abort controller for this query execution\n const queryController = new AbortController();\n\n // First update loading state and clear results\n set((state) =>\n produce(state, (draft) => {\n draft.sqlEditor.selectedTable = undefined;\n draft.sqlEditor.queryResult = {\n status: 'loading',\n isBeingAborted: false,\n controller: queryController,\n };\n draft.config.sqlEditor.lastExecutedQuery = query;\n }),\n );\n\n let queryResult: QueryResult;\n try {\n const connector = await get().db.getConnector();\n const signal = queryController.signal;\n\n const statements = splitSqlStatements(query);\n const allButLastStatements = statements.slice(0, -1);\n const lastQueryStatement = statements[\n statements.length - 1\n ] as string;\n\n if (!statements?.length) {\n throw new Error('Empty query');\n }\n\n // Execute all but the last statements with cancellation support\n for (const statement of allButLastStatements) {\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n await connector.query(statement, {signal}).result;\n }\n\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n\n const parsedLastStatement =\n await get().db.sqlSelectToJson(lastQueryStatement);\n\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n\n const isValidSelectQuery = !parsedLastStatement.error;\n\n if (isValidSelectQuery) {\n const result = await connector.query(\n makeLimitQuery(lastQueryStatement, {\n sanitize: false, // should already be sanitized\n limit: get().sqlEditor.queryResultLimit,\n }),\n {signal},\n ).result;\n queryResult = {\n status: 'success',\n type: 'select',\n lastQueryStatement,\n result,\n };\n } else {\n if (\n parsedLastStatement.error &&\n parsedLastStatement.error_type !== 'not implemented'\n ) {\n throw (\n `${parsedLastStatement.error_type} ${parsedLastStatement.error_subtype}: ${parsedLastStatement.error_message}` +\n `\\n${getSqlErrorWithPointer(lastQueryStatement, Number(parsedLastStatement.position)).formatted}`\n );\n }\n\n const result = await connector.query(lastQueryStatement, {signal})\n .result;\n\n // EXPLAIN and PRAGMA are not detected as select queries\n // and we cannot wrap them in a SELECT * FROM,\n // but we can still execute them and return the result\n if (/^(EXPLAIN)/i.test(lastQueryStatement)) {\n queryResult = {\n status: 'success',\n type: 'explain',\n lastQueryStatement,\n result,\n };\n } else if (/^(PRAGMA)/i.test(lastQueryStatement)) {\n queryResult = {\n status: 'success',\n type: 'pragma',\n lastQueryStatement,\n result,\n };\n } else {\n queryResult = {\n status: 'success',\n type: 'exec',\n lastQueryStatement,\n };\n }\n }\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n // Refresh table schemas if there are multiple statements or if the\n // last statement is not a select query\n if (statements.length > 1 || !isValidSelectQuery) {\n get().db.refreshTableSchemas();\n }\n if (signal.aborted) {\n throw new Error('Query aborted');\n }\n } catch (e) {\n console.error(e);\n const errorMessage = e instanceof Error ? e.message : String(e);\n\n if (\n errorMessage === 'Query aborted' ||\n queryController.signal.aborted\n ) {\n queryResult = {status: 'aborted'};\n } else {\n queryResult = {\n status: 'error',\n error: errorMessage,\n };\n }\n }\n\n set((state) => ({\n ...state,\n sqlEditor: {...state.sqlEditor, queryResult},\n }));\n },\n },\n };\n });\n}\n\ntype ProjectConfigWithSqlEditor = BaseProjectConfig & SqlEditorSliceConfig;\ntype ProjectStateWithSqlEditor =\n ProjectBuilderState<ProjectConfigWithSqlEditor> & SqlEditorSliceState;\n\nexport function useStoreWithSqlEditor<T>(\n selector: (state: ProjectStateWithSqlEditor) => T,\n): T {\n return useBaseProjectBuilderStore<\n BaseProjectConfig & SqlEditorSliceConfig,\n ProjectBuilderState<ProjectConfigWithSqlEditor>,\n T\n >((state) => selector(state as unknown as ProjectStateWithSqlEditor));\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"SqlMonacoEditor.d.ts","sourceRoot":"","sources":["../src/SqlMonacoEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAQ/D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAEhD,MAAM,WAAW,oBACf,SAAQ,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAC3C;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM;QACvB,YAAY,EAAE,SAAS,EAAE,CAAC;KAC3B,CAAC;CACH;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAsP1D,CAAC"}
1
+ {"version":3,"file":"SqlMonacoEditor.d.ts","sourceRoot":"","sources":["../src/SqlMonacoEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAQ/D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAGhD,MAAM,WAAW,oBACf,SAAQ,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAC3C;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM;QACvB,YAAY,EAAE,SAAS,EAAE,CAAC;KAC3B,CAAC;CACH;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAsP1D,CAAC"}
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useRef } from 'react';
3
3
  import { MonacoEditor } from '@sqlrooms/monaco-editor';
4
4
  import { DUCKDB_KEYWORDS, DUCKDB_FUNCTIONS, SQL_LANGUAGE_CONFIGURATION, } from './constants/duckdb-dialect';
5
+ import { cn } from '@sqlrooms/ui';
5
6
  /**
6
7
  * A Monaco editor for editing SQL with DuckDB syntax highlighting and autocompletion
7
8
  * This is an internal component used by SqlEditor
@@ -196,7 +197,7 @@ export const SqlMonacoEditor = ({ customKeywords = [], customFunctions = [], tab
196
197
  onMount,
197
198
  registerCompletionProvider,
198
199
  ]);
199
- return (_jsx(MonacoEditor, { language: "sql", onMount: handleEditorDidMount, className: className, options: {
200
+ return (_jsx(MonacoEditor, { language: "sql", onMount: handleEditorDidMount, className: cn('h-full', className), options: {
200
201
  formatOnPaste: true,
201
202
  formatOnType: true,
202
203
  wordWrap: 'on',
@@ -1 +1 @@
1
- {"version":3,"file":"SqlMonacoEditor.js","sourceRoot":"","sources":["../src/SqlMonacoEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useCallback, useEffect, useRef} from 'react';\nimport {MonacoEditor} from '@sqlrooms/monaco-editor';\nimport type {MonacoEditorProps} from '@sqlrooms/monaco-editor';\nimport type {OnMount} from '@monaco-editor/react';\nimport type * as Monaco from 'monaco-editor';\nimport {\n DUCKDB_KEYWORDS,\n DUCKDB_FUNCTIONS,\n SQL_LANGUAGE_CONFIGURATION,\n} from './constants/duckdb-dialect';\nimport type {DataTable} from '@sqlrooms/duckdb';\n\nexport interface SqlMonacoEditorProps\n extends Omit<MonacoEditorProps, 'language'> {\n /**\n * Custom SQL keywords to add to the completion provider\n */\n customKeywords?: string[];\n /**\n * Custom SQL functions to add to the completion provider\n */\n customFunctions?: string[];\n /**\n * Table schemas for autocompletion\n */\n tableSchemas?: DataTable[];\n /**\n * Callback to get the latest table schemas\n * This is called from within provideCompletionItems to ensure we have the latest data\n */\n getLatestSchemas?: () => {\n tableSchemas: DataTable[];\n };\n}\n\n/**\n * A Monaco editor for editing SQL with DuckDB syntax highlighting and autocompletion\n * This is an internal component used by SqlEditor\n */\nexport const SqlMonacoEditor: React.FC<SqlMonacoEditorProps> = ({\n customKeywords = [],\n customFunctions = [],\n tableSchemas = [],\n getLatestSchemas,\n onMount,\n className,\n ...props\n}) => {\n // Store references to editor and monaco\n const editorRef = useRef<any>(null);\n const monacoRef = useRef<any>(null);\n const disposableRef = useRef<any>(null);\n\n // Function to register the completion provider\n const registerCompletionProvider = useCallback(() => {\n if (!editorRef.current || !monacoRef.current) return;\n\n const monaco = monacoRef.current;\n\n // Dispose previous provider if it exists\n if (disposableRef.current) {\n disposableRef.current.dispose();\n }\n\n // Register SQL completion provider\n const disposable = monaco.languages.registerCompletionItemProvider('sql', {\n triggerCharacters: [' ', '.', ',', '(', '='],\n provideCompletionItems: (model: any, position: any) => {\n try {\n // Get the latest schemas if the callback is provided\n let currentSchemas = tableSchemas;\n\n if (getLatestSchemas) {\n const latest = getLatestSchemas();\n currentSchemas = latest.tableSchemas;\n }\n\n const suggestions: Monaco.languages.CompletionItem[] = [];\n const word = model.getWordUntilPosition(position);\n const range = {\n startLineNumber: position.lineNumber,\n endLineNumber: position.lineNumber,\n startColumn: word.startColumn,\n endColumn: word.endColumn,\n };\n\n // Get the text before the cursor to determine context\n const lineContent = model.getLineContent(position.lineNumber);\n const textBeforeCursor = lineContent\n .substring(0, position.column - 1)\n .trim()\n .toLowerCase();\n\n // Check if we're after a FROM, JOIN, or similar clause to prioritize table suggestions\n const isTableContext = /\\b(from|join|into|update|table)\\s+\\w*$/.test(\n textBeforeCursor,\n );\n\n // Check if we're after a table name and period to prioritize column suggestions\n const isColumnContext = /\\b(\\w+)\\.\\w*$/.test(textBeforeCursor);\n\n // Combine keywords and functions with custom ones\n const keywords = [...DUCKDB_KEYWORDS, ...customKeywords];\n const functions = [...DUCKDB_FUNCTIONS, ...customFunctions];\n\n // Add keyword suggestions (if not in a specific context)\n if (!isColumnContext) {\n keywords.forEach((keyword) => {\n suggestions.push({\n label: keyword,\n kind: monaco.languages.CompletionItemKind.Keyword,\n insertText: keyword,\n range: range,\n detail: 'Keyword',\n sortText: isTableContext ? 'z' + keyword : 'a' + keyword, // Lower priority in table context\n });\n });\n }\n\n // Add function suggestions (if not in a specific context)\n if (!isColumnContext) {\n functions.forEach((func) => {\n suggestions.push({\n label: func,\n kind: monaco.languages.CompletionItemKind.Function,\n insertText: func,\n range: range,\n detail: 'Function',\n sortText: isTableContext ? 'z' + func : 'b' + func, // Lower priority in table context\n });\n });\n }\n\n // Add table and column suggestions from schemas\n currentSchemas.forEach((table) => {\n const tableName = table.tableName;\n\n // Add table suggestion\n suggestions.push({\n label: tableName,\n kind: monaco.languages.CompletionItemKind.Class,\n insertText: tableName,\n range: range,\n detail: 'Table',\n documentation: {\n value: `Table: ${tableName}`,\n isTrusted: true,\n },\n sortText: isTableContext ? 'a' + tableName : 'c' + tableName, // Higher priority in table context\n });\n\n // Extract table name from context if we're in a column context\n let contextTableName = '';\n if (isColumnContext) {\n const match = textBeforeCursor.match(/\\b(\\w+)\\.\\w*$/);\n if (match && match[1]) {\n contextTableName = match[1];\n }\n }\n\n // Only add columns for the current table if we're in a column context\n if (!isColumnContext || contextTableName === tableName) {\n // Add column suggestions\n table.columns.forEach((column) => {\n const columnName = column.name;\n const columnType = column.type;\n\n suggestions.push({\n label: columnName,\n kind: monaco.languages.CompletionItemKind.Field,\n insertText: columnName,\n range: range,\n detail: `Column (${columnType})`,\n documentation: {\n value: `Column from table ${tableName}`,\n isTrusted: true,\n },\n sortText:\n isColumnContext && contextTableName === tableName\n ? 'a' + columnName\n : 'd' + columnName,\n });\n\n // Only add table.column suggestions if not in a column context\n if (!isColumnContext) {\n suggestions.push({\n label: `${tableName}.${columnName}`,\n kind: monaco.languages.CompletionItemKind.Field,\n insertText: `${tableName}.${columnName}`,\n range: range,\n detail: `Column (${columnType})`,\n documentation: {\n value: `Column from table ${tableName}`,\n isTrusted: true,\n },\n sortText: 'e' + tableName + columnName,\n });\n }\n });\n }\n });\n\n return {\n suggestions,\n };\n } catch (error) {\n console.error('Error in SQL completion provider:', error);\n return {suggestions: []};\n }\n },\n });\n\n // Store the disposable to clean up later\n disposableRef.current = disposable;\n }, [customKeywords, customFunctions, tableSchemas, getLatestSchemas]);\n\n // Re-register completion provider when tableSchemas change\n useEffect(() => {\n if (editorRef.current && monacoRef.current) {\n registerCompletionProvider();\n }\n }, [tableSchemas, registerCompletionProvider]);\n\n // Handle editor mounting to configure SQL language features\n const handleEditorDidMount = useCallback<OnMount>(\n (editor, monaco) => {\n // Store references\n editorRef.current = editor;\n monacoRef.current = monaco;\n\n // Register SQL language if not already registered\n if (\n !monaco.languages.getLanguages().some((lang: any) => lang.id === 'sql')\n ) {\n monaco.languages.register({id: 'sql'});\n }\n\n // Combine keywords and functions with custom ones\n const keywords = [...DUCKDB_KEYWORDS, ...customKeywords];\n const functions = [...DUCKDB_FUNCTIONS, ...customFunctions];\n\n // Set the language configuration\n monaco.languages.setMonarchTokensProvider('sql', {\n ...SQL_LANGUAGE_CONFIGURATION,\n keywords,\n builtinFunctions: functions,\n } as any); // Using 'as any' to bypass the type checking issue\n\n // Register the completion provider\n registerCompletionProvider();\n\n // Store the disposable to clean up later if needed\n editor.onDidDispose(() => {\n if (disposableRef.current) {\n disposableRef.current.dispose();\n }\n });\n\n // Call the original onMount if provided\n if (onMount) {\n onMount(editor, monaco);\n }\n },\n [\n customKeywords,\n customFunctions,\n tableSchemas,\n onMount,\n registerCompletionProvider,\n ],\n );\n\n return (\n <MonacoEditor\n language=\"sql\"\n onMount={handleEditorDidMount}\n className={className}\n options={{\n formatOnPaste: true,\n formatOnType: true,\n wordWrap: 'on',\n }}\n {...props}\n />\n );\n};\n"]}
1
+ {"version":3,"file":"SqlMonacoEditor.js","sourceRoot":"","sources":["../src/SqlMonacoEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useCallback, useEffect, useRef} from 'react';\nimport {MonacoEditor} from '@sqlrooms/monaco-editor';\nimport type {MonacoEditorProps} from '@sqlrooms/monaco-editor';\nimport type {OnMount} from '@monaco-editor/react';\nimport type * as Monaco from 'monaco-editor';\nimport {\n DUCKDB_KEYWORDS,\n DUCKDB_FUNCTIONS,\n SQL_LANGUAGE_CONFIGURATION,\n} from './constants/duckdb-dialect';\nimport type {DataTable} from '@sqlrooms/duckdb';\nimport {cn} from '@sqlrooms/ui';\n\nexport interface SqlMonacoEditorProps\n extends Omit<MonacoEditorProps, 'language'> {\n /**\n * Custom SQL keywords to add to the completion provider\n */\n customKeywords?: string[];\n /**\n * Custom SQL functions to add to the completion provider\n */\n customFunctions?: string[];\n /**\n * Table schemas for autocompletion\n */\n tableSchemas?: DataTable[];\n /**\n * Callback to get the latest table schemas\n * This is called from within provideCompletionItems to ensure we have the latest data\n */\n getLatestSchemas?: () => {\n tableSchemas: DataTable[];\n };\n}\n\n/**\n * A Monaco editor for editing SQL with DuckDB syntax highlighting and autocompletion\n * This is an internal component used by SqlEditor\n */\nexport const SqlMonacoEditor: React.FC<SqlMonacoEditorProps> = ({\n customKeywords = [],\n customFunctions = [],\n tableSchemas = [],\n getLatestSchemas,\n onMount,\n className,\n ...props\n}) => {\n // Store references to editor and monaco\n const editorRef = useRef<any>(null);\n const monacoRef = useRef<any>(null);\n const disposableRef = useRef<any>(null);\n\n // Function to register the completion provider\n const registerCompletionProvider = useCallback(() => {\n if (!editorRef.current || !monacoRef.current) return;\n\n const monaco = monacoRef.current;\n\n // Dispose previous provider if it exists\n if (disposableRef.current) {\n disposableRef.current.dispose();\n }\n\n // Register SQL completion provider\n const disposable = monaco.languages.registerCompletionItemProvider('sql', {\n triggerCharacters: [' ', '.', ',', '(', '='],\n provideCompletionItems: (model: any, position: any) => {\n try {\n // Get the latest schemas if the callback is provided\n let currentSchemas = tableSchemas;\n\n if (getLatestSchemas) {\n const latest = getLatestSchemas();\n currentSchemas = latest.tableSchemas;\n }\n\n const suggestions: Monaco.languages.CompletionItem[] = [];\n const word = model.getWordUntilPosition(position);\n const range = {\n startLineNumber: position.lineNumber,\n endLineNumber: position.lineNumber,\n startColumn: word.startColumn,\n endColumn: word.endColumn,\n };\n\n // Get the text before the cursor to determine context\n const lineContent = model.getLineContent(position.lineNumber);\n const textBeforeCursor = lineContent\n .substring(0, position.column - 1)\n .trim()\n .toLowerCase();\n\n // Check if we're after a FROM, JOIN, or similar clause to prioritize table suggestions\n const isTableContext = /\\b(from|join|into|update|table)\\s+\\w*$/.test(\n textBeforeCursor,\n );\n\n // Check if we're after a table name and period to prioritize column suggestions\n const isColumnContext = /\\b(\\w+)\\.\\w*$/.test(textBeforeCursor);\n\n // Combine keywords and functions with custom ones\n const keywords = [...DUCKDB_KEYWORDS, ...customKeywords];\n const functions = [...DUCKDB_FUNCTIONS, ...customFunctions];\n\n // Add keyword suggestions (if not in a specific context)\n if (!isColumnContext) {\n keywords.forEach((keyword) => {\n suggestions.push({\n label: keyword,\n kind: monaco.languages.CompletionItemKind.Keyword,\n insertText: keyword,\n range: range,\n detail: 'Keyword',\n sortText: isTableContext ? 'z' + keyword : 'a' + keyword, // Lower priority in table context\n });\n });\n }\n\n // Add function suggestions (if not in a specific context)\n if (!isColumnContext) {\n functions.forEach((func) => {\n suggestions.push({\n label: func,\n kind: monaco.languages.CompletionItemKind.Function,\n insertText: func,\n range: range,\n detail: 'Function',\n sortText: isTableContext ? 'z' + func : 'b' + func, // Lower priority in table context\n });\n });\n }\n\n // Add table and column suggestions from schemas\n currentSchemas.forEach((table) => {\n const tableName = table.tableName;\n\n // Add table suggestion\n suggestions.push({\n label: tableName,\n kind: monaco.languages.CompletionItemKind.Class,\n insertText: tableName,\n range: range,\n detail: 'Table',\n documentation: {\n value: `Table: ${tableName}`,\n isTrusted: true,\n },\n sortText: isTableContext ? 'a' + tableName : 'c' + tableName, // Higher priority in table context\n });\n\n // Extract table name from context if we're in a column context\n let contextTableName = '';\n if (isColumnContext) {\n const match = textBeforeCursor.match(/\\b(\\w+)\\.\\w*$/);\n if (match && match[1]) {\n contextTableName = match[1];\n }\n }\n\n // Only add columns for the current table if we're in a column context\n if (!isColumnContext || contextTableName === tableName) {\n // Add column suggestions\n table.columns.forEach((column) => {\n const columnName = column.name;\n const columnType = column.type;\n\n suggestions.push({\n label: columnName,\n kind: monaco.languages.CompletionItemKind.Field,\n insertText: columnName,\n range: range,\n detail: `Column (${columnType})`,\n documentation: {\n value: `Column from table ${tableName}`,\n isTrusted: true,\n },\n sortText:\n isColumnContext && contextTableName === tableName\n ? 'a' + columnName\n : 'd' + columnName,\n });\n\n // Only add table.column suggestions if not in a column context\n if (!isColumnContext) {\n suggestions.push({\n label: `${tableName}.${columnName}`,\n kind: monaco.languages.CompletionItemKind.Field,\n insertText: `${tableName}.${columnName}`,\n range: range,\n detail: `Column (${columnType})`,\n documentation: {\n value: `Column from table ${tableName}`,\n isTrusted: true,\n },\n sortText: 'e' + tableName + columnName,\n });\n }\n });\n }\n });\n\n return {\n suggestions,\n };\n } catch (error) {\n console.error('Error in SQL completion provider:', error);\n return {suggestions: []};\n }\n },\n });\n\n // Store the disposable to clean up later\n disposableRef.current = disposable;\n }, [customKeywords, customFunctions, tableSchemas, getLatestSchemas]);\n\n // Re-register completion provider when tableSchemas change\n useEffect(() => {\n if (editorRef.current && monacoRef.current) {\n registerCompletionProvider();\n }\n }, [tableSchemas, registerCompletionProvider]);\n\n // Handle editor mounting to configure SQL language features\n const handleEditorDidMount = useCallback<OnMount>(\n (editor, monaco) => {\n // Store references\n editorRef.current = editor;\n monacoRef.current = monaco;\n\n // Register SQL language if not already registered\n if (\n !monaco.languages.getLanguages().some((lang: any) => lang.id === 'sql')\n ) {\n monaco.languages.register({id: 'sql'});\n }\n\n // Combine keywords and functions with custom ones\n const keywords = [...DUCKDB_KEYWORDS, ...customKeywords];\n const functions = [...DUCKDB_FUNCTIONS, ...customFunctions];\n\n // Set the language configuration\n monaco.languages.setMonarchTokensProvider('sql', {\n ...SQL_LANGUAGE_CONFIGURATION,\n keywords,\n builtinFunctions: functions,\n } as any); // Using 'as any' to bypass the type checking issue\n\n // Register the completion provider\n registerCompletionProvider();\n\n // Store the disposable to clean up later if needed\n editor.onDidDispose(() => {\n if (disposableRef.current) {\n disposableRef.current.dispose();\n }\n });\n\n // Call the original onMount if provided\n if (onMount) {\n onMount(editor, monaco);\n }\n },\n [\n customKeywords,\n customFunctions,\n tableSchemas,\n onMount,\n registerCompletionProvider,\n ],\n );\n\n return (\n <MonacoEditor\n language=\"sql\"\n onMount={handleEditorDidMount}\n className={cn('h-full', className)}\n options={{\n formatOnPaste: true,\n formatOnType: true,\n wordWrap: 'on',\n }}\n {...props}\n />\n );\n};\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreateTableModal.d.ts","sourceRoot":"","sources":["../../src/components/CreateTableModal.tsx"],"names":[],"mappings":"AACA,OAAO,EAAC,kBAAkB,EAAC,MAAM,2BAA2B,CAAC;AAmB7D,OAAO,EAAC,EAAE,EAAc,MAAM,OAAO,CAAC;AAkBtC,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC,qBAAqB,EAAE,CACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,MAAM,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AAEF,QAAA,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CA0G/C,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -4,7 +4,7 @@ import { Alert, AlertDescription, Button, Dialog, DialogContent, DialogDescripti
4
4
  import { useCallback } from 'react';
5
5
  import { useForm } from 'react-hook-form';
6
6
  import * as z from 'zod';
7
- import { SqlMonacoEditor } from './SqlMonacoEditor';
7
+ import { SqlMonacoEditor } from '../SqlMonacoEditor';
8
8
  const VALID_TABLE_OR_COLUMN_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]{0,62}$/;
9
9
  const formSchema = z.object({
10
10
  tableName: z
@@ -35,15 +35,7 @@ const CreateTableModal = (props) => {
35
35
  }, [onAddOrUpdateSqlQuery, editDataSource?.tableName, onClose, form]);
36
36
  return (_jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: _jsx(DialogContent, { className: "sm:max-w-[800px]", children: _jsx(Form, { ...form, children: _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-4", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: editDataSource
37
37
  ? 'Edit table query'
38
- : 'Create table from query' }), !editDataSource && (_jsx(DialogDescription, { children: "Create a new table from the results of an SQL query." }))] }), form.formState.errors.root && (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: form.formState.errors.root.message }) })), _jsx(FormField
39
- // @ts-ignore
40
- , {
41
- // @ts-ignore
42
- control: form.control, name: "tableName", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Table name:" }), _jsx(FormControl, { children: _jsx(Input, { ...field, className: "font-mono", autoFocus: true }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField
43
- // @ts-ignore
44
- , {
45
- // @ts-ignore
46
- control: form.control, name: "query", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "SQL query:" }), _jsx(FormControl, { children: _jsx(SqlMonacoEditor, { value: field.value, onChange: field.onChange, className: "min-h-[200px]", options: {
38
+ : 'Create table from query' }), !editDataSource && (_jsx(DialogDescription, { children: "Create a new table from the results of an SQL query." }))] }), form.formState.errors.root && (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { className: "whitespace-pre-wrap font-mono text-xs", children: form.formState.errors.root.message }) })), _jsx(FormField, { control: form.control, name: "tableName", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Table name:" }), _jsx(FormControl, { children: _jsx(Input, { ...field, className: "font-mono", autoFocus: true }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "query", render: ({ field }) => (_jsxs(FormItem, { className: "flex flex-col", children: [_jsx(FormLabel, { children: "SQL query:" }), _jsx(FormControl, { children: _jsx(SqlMonacoEditor, { value: field.value, onChange: field.onChange, className: "h-[200px] w-full", options: {
47
39
  scrollBeyondLastLine: false,
48
40
  automaticLayout: true,
49
41
  minimap: { enabled: false },