@sqlrooms/monaco-editor 0.7.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/LICENSE.md +9 -0
- package/README.md +86 -0
- package/dist/components/JsonMonacoEditor.d.ts +17 -0
- package/dist/components/JsonMonacoEditor.d.ts.map +1 -0
- package/dist/components/JsonMonacoEditor.js +38 -0
- package/dist/components/JsonMonacoEditor.js.map +1 -0
- package/dist/components/MonacoEditor.d.ts +46 -0
- package/dist/components/MonacoEditor.d.ts.map +1 -0
- package/dist/components/MonacoEditor.js +154 -0
- package/dist/components/MonacoEditor.js.map +1 -0
- package/dist/components/SqlMonacoEditor.d.ts +27 -0
- package/dist/components/SqlMonacoEditor.d.ts.map +1 -0
- package/dist/components/SqlMonacoEditor.js +172 -0
- package/dist/components/SqlMonacoEditor.js.map +1 -0
- package/dist/constants/duckdb.d.ts +73 -0
- package/dist/constants/duckdb.d.ts.map +1 -0
- package/dist/constants/duckdb.js +392 -0
- package/dist/constants/duckdb.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/color-utils.d.ts +33 -0
- package/dist/utils/color-utils.d.ts.map +1 -0
- package/dist/utils/color-utils.js +263 -0
- package/dist/utils/color-utils.js.map +1 -0
- package/package.json +37 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Ilya Boyandin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @sqlrooms/monaco-editor
|
|
2
|
+
|
|
3
|
+
Monaco Editor components for SQLRooms, including specialized editors for JSON.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sqlrooms/monaco-editor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Components
|
|
12
|
+
|
|
13
|
+
### MonacoEditor
|
|
14
|
+
|
|
15
|
+
A base Monaco Editor component with common functionality.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import {MonacoEditor} from '@sqlrooms/monaco-editor';
|
|
19
|
+
|
|
20
|
+
function MyComponent() {
|
|
21
|
+
return (
|
|
22
|
+
<MonacoEditor
|
|
23
|
+
className="h-[400px]"
|
|
24
|
+
language="javascript"
|
|
25
|
+
value="// Your code here"
|
|
26
|
+
onChange={(value) => console.log(value)}
|
|
27
|
+
/>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### JsonMonacoEditor
|
|
33
|
+
|
|
34
|
+
A specialized Monaco Editor for editing JSON with schema validation.
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import {JsonMonacoEditor} from '@sqlrooms/monaco-editor';
|
|
38
|
+
|
|
39
|
+
function MyJsonEditor() {
|
|
40
|
+
const schema = {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
name: {type: 'string'},
|
|
44
|
+
age: {type: 'number'},
|
|
45
|
+
},
|
|
46
|
+
required: ['name'],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<JsonMonacoEditor
|
|
51
|
+
className="h-[400px]"
|
|
52
|
+
value={{name: 'John', age: 30}}
|
|
53
|
+
schema={schema}
|
|
54
|
+
onChange={(value) => console.log(value)}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Props
|
|
61
|
+
|
|
62
|
+
### MonacoEditor Props
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Default | Description |
|
|
65
|
+
| --------- | -------------------- | ------------ | ---------------------------------------- |
|
|
66
|
+
| className | string | '' | CSS class name for the editor container |
|
|
67
|
+
| language | string | 'javascript' | The language of the editor |
|
|
68
|
+
| theme | 'vs-dark' \| 'light' | 'vs-dark' | The theme of the editor |
|
|
69
|
+
| value | string | '' | The value of the editor |
|
|
70
|
+
| readOnly | boolean | false | Whether the editor is read-only |
|
|
71
|
+
| options | object | {} | Additional options for the editor |
|
|
72
|
+
| onMount | function | - | Callback when the editor is mounted |
|
|
73
|
+
| onChange | function | - | Callback when the editor content changes |
|
|
74
|
+
|
|
75
|
+
### JsonMonacoEditor Props
|
|
76
|
+
|
|
77
|
+
Extends `MonacoEditorProps` with:
|
|
78
|
+
|
|
79
|
+
| Prop | Type | Default | Description |
|
|
80
|
+
| ------ | ---------------- | ------- | ----------------------------------- |
|
|
81
|
+
| schema | object | - | The JSON schema to validate against |
|
|
82
|
+
| value | string \| object | '' | The JSON value to edit |
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { MonacoEditorProps } from './MonacoEditor';
|
|
3
|
+
export interface JsonMonacoEditorProps extends Omit<MonacoEditorProps, 'language' | 'value'> {
|
|
4
|
+
/**
|
|
5
|
+
* The JSON schema to validate against
|
|
6
|
+
*/
|
|
7
|
+
schema?: object;
|
|
8
|
+
/**
|
|
9
|
+
* The JSON value to edit
|
|
10
|
+
*/
|
|
11
|
+
value?: string | object;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A Monaco editor for editing JSON with schema validation
|
|
15
|
+
*/
|
|
16
|
+
export declare const JsonMonacoEditor: React.FC<JsonMonacoEditorProps>;
|
|
17
|
+
//# sourceMappingURL=JsonMonacoEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonMonacoEditor.d.ts","sourceRoot":"","sources":["../../src/components/JsonMonacoEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAe,iBAAiB,EAAC,MAAM,gBAAgB,CAAC;AAG/D,MAAM,WAAW,qBACf,SAAQ,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,OAAO,CAAC;IACrD;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAmD5D,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MonacoEditor } from './MonacoEditor';
|
|
3
|
+
/**
|
|
4
|
+
* A Monaco editor for editing JSON with schema validation
|
|
5
|
+
*/
|
|
6
|
+
export const JsonMonacoEditor = ({ schema, value = '', onMount, className, ...props }) => {
|
|
7
|
+
// Convert object value to string if needed
|
|
8
|
+
const stringValue = typeof value === 'object' ? JSON.stringify(value, null, 2) : value;
|
|
9
|
+
// Handle editor mounting to configure JSON schema
|
|
10
|
+
const handleEditorDidMount = (editor, monaco) => {
|
|
11
|
+
if (schema) {
|
|
12
|
+
// Configure JSON schema validation
|
|
13
|
+
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
|
|
14
|
+
validate: true,
|
|
15
|
+
schemas: [
|
|
16
|
+
{
|
|
17
|
+
uri: 'http://myserver/schema.json',
|
|
18
|
+
fileMatch: ['*'],
|
|
19
|
+
schema: schema,
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// Format the document on initial load
|
|
25
|
+
setTimeout(() => {
|
|
26
|
+
editor.getAction('editor.action.formatDocument')?.run();
|
|
27
|
+
}, 100);
|
|
28
|
+
// Call the original onMount if provided
|
|
29
|
+
if (onMount) {
|
|
30
|
+
onMount(editor, monaco);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return (_jsx(MonacoEditor, { language: "json", value: stringValue, onMount: handleEditorDidMount, className: className, options: {
|
|
34
|
+
formatOnPaste: true,
|
|
35
|
+
formatOnType: true,
|
|
36
|
+
}, ...props }));
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=JsonMonacoEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JsonMonacoEditor.js","sourceRoot":"","sources":["../../src/components/JsonMonacoEditor.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAe/D;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAoC,CAAC,EAChE,MAAM,EACN,KAAK,GAAG,EAAE,EACV,OAAO,EACP,SAAS,EACT,GAAG,KAAK,EACT,EAAE,EAAE;IACH,2CAA2C;IAC3C,MAAM,WAAW,GACf,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAErE,kDAAkD;IAClD,MAAM,oBAAoB,GAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;QACvD,IAAI,MAAM,EAAE,CAAC;YACX,mCAAmC;YACnC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC;gBACvD,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE;oBACP;wBACE,GAAG,EAAE,6BAA6B;wBAClC,SAAS,EAAE,CAAC,GAAG,CAAC;wBAChB,MAAM,EAAE,MAAa;qBACtB;iBACF;aACF,CAAC,CAAC;QACL,CAAC;QAED,sCAAsC;QACtC,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,SAAS,CAAC,8BAA8B,CAAC,EAAE,GAAG,EAAE,CAAC;QAC1D,CAAC,EAAE,GAAG,CAAC,CAAC;QAER,wCAAwC;QACxC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,KAAC,YAAY,IACX,QAAQ,EAAC,MAAM,EACf,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE;YACP,aAAa,EAAE,IAAI;YACnB,YAAY,EAAE,IAAI;SACnB,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import React from 'react';\nimport {MonacoEditor, MonacoEditorProps} from './MonacoEditor';\nimport {OnMount} from '@monaco-editor/react';\n\nexport interface JsonMonacoEditorProps\n extends Omit<MonacoEditorProps, 'language' | 'value'> {\n /**\n * The JSON schema to validate against\n */\n schema?: object;\n /**\n * The JSON value to edit\n */\n value?: string | object;\n}\n\n/**\n * A Monaco editor for editing JSON with schema validation\n */\nexport const JsonMonacoEditor: React.FC<JsonMonacoEditorProps> = ({\n schema,\n value = '',\n onMount,\n className,\n ...props\n}) => {\n // Convert object value to string if needed\n const stringValue =\n typeof value === 'object' ? JSON.stringify(value, null, 2) : value;\n\n // Handle editor mounting to configure JSON schema\n const handleEditorDidMount: OnMount = (editor, monaco) => {\n if (schema) {\n // Configure JSON schema validation\n monaco.languages.json.jsonDefaults.setDiagnosticsOptions({\n validate: true,\n schemas: [\n {\n uri: 'http://myserver/schema.json',\n fileMatch: ['*'],\n schema: schema as any,\n },\n ],\n });\n }\n\n // Format the document on initial load\n setTimeout(() => {\n editor.getAction('editor.action.formatDocument')?.run();\n }, 100);\n\n // Call the original onMount if provided\n if (onMount) {\n onMount(editor, monaco);\n }\n };\n\n return (\n <MonacoEditor\n language=\"json\"\n value={stringValue}\n onMount={handleEditorDidMount}\n className={className}\n options={{\n formatOnPaste: true,\n formatOnType: true,\n }}\n {...props}\n />\n );\n};\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { EditorProps, OnChange, OnMount } from '@monaco-editor/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export interface MonacoEditorProps extends Omit<EditorProps, 'onMount'> {
|
|
4
|
+
/**
|
|
5
|
+
* Callback when the editor is mounted
|
|
6
|
+
*/
|
|
7
|
+
onMount?: OnMount;
|
|
8
|
+
/**
|
|
9
|
+
* Callback when the editor content changes
|
|
10
|
+
*/
|
|
11
|
+
onChange?: OnChange;
|
|
12
|
+
/**
|
|
13
|
+
* CSS class name for the editor container
|
|
14
|
+
* @default ''
|
|
15
|
+
*/
|
|
16
|
+
className?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The language of the editor
|
|
19
|
+
* @default 'javascript'
|
|
20
|
+
*/
|
|
21
|
+
language?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The theme of the editor
|
|
24
|
+
* Can be explicitly set or will automatically use the app theme if not provided
|
|
25
|
+
* @default 'vs-dark'
|
|
26
|
+
*/
|
|
27
|
+
theme?: 'vs-dark' | 'light';
|
|
28
|
+
/**
|
|
29
|
+
* The value of the editor
|
|
30
|
+
*/
|
|
31
|
+
value?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Whether the editor is read-only
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
readOnly?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Additional options for the editor
|
|
39
|
+
*/
|
|
40
|
+
options?: Record<string, any>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A wrapper around the Monaco Editor component
|
|
44
|
+
*/
|
|
45
|
+
export declare const MonacoEditor: React.FC<MonacoEditorProps>;
|
|
46
|
+
//# sourceMappingURL=MonacoEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MonacoEditor.d.ts","sourceRoot":"","sources":["../../src/components/MonacoEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,QAAQ,EACR,OAAO,EAER,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAA0B,MAAM,OAAO,CAAC;AAe/C,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC;IACrE;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA0MpD,CAAC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Editor, loader, } from '@monaco-editor/react';
|
|
3
|
+
import { Spinner, cn, useTheme } from '@sqlrooms/ui';
|
|
4
|
+
import { useEffect, useRef } from 'react';
|
|
5
|
+
import { getCssColor, getJsonEditorTheme, getMenuColors, getMonospaceFont, } from '../utils/color-utils';
|
|
6
|
+
// Configure the Monaco loader
|
|
7
|
+
loader.config({
|
|
8
|
+
paths: {
|
|
9
|
+
vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs',
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* A wrapper around the Monaco Editor component
|
|
14
|
+
*/
|
|
15
|
+
export const MonacoEditor = ({ className, language = 'javascript', theme: explicitTheme, value = '', readOnly = false, onMount, onChange, options = {}, ...props }) => {
|
|
16
|
+
// Get the app theme from the ThemeProvider
|
|
17
|
+
const { theme: appTheme } = useTheme();
|
|
18
|
+
// If a theme is explicitly provided, use it. Otherwise, determine from the app theme
|
|
19
|
+
const theme = explicitTheme ||
|
|
20
|
+
(appTheme === 'dark' ||
|
|
21
|
+
(appTheme === 'system' &&
|
|
22
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
23
|
+
? 'vs-dark'
|
|
24
|
+
: 'light');
|
|
25
|
+
const editorRef = useRef(null);
|
|
26
|
+
const monacoRef = useRef(null);
|
|
27
|
+
const handleEditorDidMount = (editor, monaco) => {
|
|
28
|
+
editorRef.current = editor;
|
|
29
|
+
monacoRef.current = monaco;
|
|
30
|
+
// Special language configuration for JSON
|
|
31
|
+
if (language === 'json') {
|
|
32
|
+
// Define a more robust tokenizer for JSON with improved rules
|
|
33
|
+
monaco.languages.setMonarchTokensProvider('json', {
|
|
34
|
+
tokenizer: {
|
|
35
|
+
root: [
|
|
36
|
+
// Property keys (strings followed by a colon)
|
|
37
|
+
[/"([^"]*)"(?=\s*:)/, 'string.key.json'],
|
|
38
|
+
// Regular string values (any quoted string not followed by a colon)
|
|
39
|
+
[/"([^"]*)"(?!\s*:)/, 'string.value.json'],
|
|
40
|
+
// Numbers (integers, decimals, and scientific notation)
|
|
41
|
+
[/-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/, 'number'],
|
|
42
|
+
// Keywords
|
|
43
|
+
[/\b(?:true|false|null)\b/, 'keyword'],
|
|
44
|
+
// Punctuation and delimiters
|
|
45
|
+
[/[{}[\],:]/, 'delimiter'],
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Define editor themes with Tailwind CSS variables
|
|
51
|
+
monaco.editor.defineTheme('sqlrooms-light', {
|
|
52
|
+
base: 'vs',
|
|
53
|
+
inherit: true,
|
|
54
|
+
rules: [],
|
|
55
|
+
colors: {
|
|
56
|
+
'editor.background': getCssColor('--background', '#ffffff'),
|
|
57
|
+
'editor.foreground': getCssColor('--foreground', '#000000'),
|
|
58
|
+
'editor.lineHighlightBackground': getCssColor('--muted', '#f5f5f5'),
|
|
59
|
+
'editorCursor.foreground': getCssColor('--primary', '#000000'),
|
|
60
|
+
'editor.selectionBackground': getCssColor('--accent', '#e3e3e3'),
|
|
61
|
+
'editorLineNumber.foreground': getCssColor('--muted-foreground', '#888888'),
|
|
62
|
+
...getMenuColors(false),
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
monaco.editor.defineTheme('sqlrooms-dark', {
|
|
66
|
+
base: 'vs-dark',
|
|
67
|
+
inherit: true,
|
|
68
|
+
rules: [],
|
|
69
|
+
colors: {
|
|
70
|
+
'editor.background': getCssColor('--background', '#1e1e1e'),
|
|
71
|
+
'editor.foreground': getCssColor('--foreground', '#d4d4d4'),
|
|
72
|
+
'editor.lineHighlightBackground': getCssColor('--muted', '#2a2a2a'),
|
|
73
|
+
'editorCursor.foreground': getCssColor('--primary', '#ffffff'),
|
|
74
|
+
'editor.selectionBackground': getCssColor('--accent', '#264f78'),
|
|
75
|
+
'editorLineNumber.foreground': getCssColor('--muted-foreground', '#858585'),
|
|
76
|
+
...getMenuColors(true),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
// Define JSON-specific themes with rich token coloring
|
|
80
|
+
monaco.editor.defineTheme('sqlrooms-json-light', getJsonEditorTheme(false));
|
|
81
|
+
monaco.editor.defineTheme('sqlrooms-json-dark', getJsonEditorTheme(true));
|
|
82
|
+
// Apply the custom theme based on content type
|
|
83
|
+
const isDark = appTheme === 'dark' ||
|
|
84
|
+
(appTheme === 'system' &&
|
|
85
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
86
|
+
// Use JSON-specific theme for JSON files
|
|
87
|
+
if (language === 'json') {
|
|
88
|
+
monaco.editor.setTheme(isDark ? 'sqlrooms-json-dark' : 'sqlrooms-json-light');
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
monaco.editor.setTheme(isDark ? 'sqlrooms-dark' : 'sqlrooms-light');
|
|
92
|
+
}
|
|
93
|
+
if (onMount) {
|
|
94
|
+
onMount(editor, monaco);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
// Apply readOnly option
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (editorRef.current) {
|
|
100
|
+
editorRef.current.updateOptions({ readOnly });
|
|
101
|
+
}
|
|
102
|
+
}, [readOnly]);
|
|
103
|
+
// Update the editor theme when app theme changes
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (editorRef.current && monacoRef.current && !explicitTheme) {
|
|
106
|
+
const isDark = appTheme === 'dark' ||
|
|
107
|
+
(appTheme === 'system' &&
|
|
108
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
109
|
+
// Use JSON-specific theme for JSON files
|
|
110
|
+
if (language === 'json') {
|
|
111
|
+
monacoRef.current.editor.setTheme(isDark ? 'sqlrooms-json-dark' : 'sqlrooms-json-light');
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
monacoRef.current.editor.setTheme(isDark ? 'sqlrooms-dark' : 'sqlrooms-light');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}, [appTheme, explicitTheme, language]);
|
|
118
|
+
// Listen for system theme changes if using system theme
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (appTheme === 'system' && !explicitTheme && monacoRef.current) {
|
|
121
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
122
|
+
const handleChange = () => {
|
|
123
|
+
if (monacoRef.current) {
|
|
124
|
+
// Use JSON-specific theme for JSON files
|
|
125
|
+
if (language === 'json') {
|
|
126
|
+
monacoRef.current.editor.setTheme(mediaQuery.matches ? 'sqlrooms-json-dark' : 'sqlrooms-json-light');
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
monacoRef.current.editor.setTheme(mediaQuery.matches ? 'sqlrooms-dark' : 'sqlrooms-light');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
// Add listener for theme changes
|
|
134
|
+
mediaQuery.addEventListener('change', handleChange);
|
|
135
|
+
// Clean up
|
|
136
|
+
return () => {
|
|
137
|
+
mediaQuery.removeEventListener('change', handleChange);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}, [appTheme, explicitTheme, language]);
|
|
141
|
+
// Get monospace font for code editor
|
|
142
|
+
const fontFamily = getMonospaceFont();
|
|
143
|
+
const defaultOptions = {
|
|
144
|
+
minimap: { enabled: false },
|
|
145
|
+
scrollBeyondLastLine: false,
|
|
146
|
+
automaticLayout: true,
|
|
147
|
+
readOnly,
|
|
148
|
+
fontFamily,
|
|
149
|
+
fontLigatures: true,
|
|
150
|
+
...options,
|
|
151
|
+
};
|
|
152
|
+
return (_jsx("div", { className: cn('w-full h-[300px]', className), children: _jsx(Editor, { height: "100%", width: "100%", language: language, theme: theme, value: value, options: defaultOptions, onMount: handleEditorDidMount, onChange: onChange, loading: _jsx(Spinner, {}), ...props }) }));
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=MonacoEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MonacoEditor.js","sourceRoot":"","sources":["../../src/components/MonacoEditor.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,MAAM,EAIN,MAAM,GACP,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAC,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAC,MAAM,cAAc,CAAC;AACnD,OAAc,EAAC,SAAS,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AAC/C,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAE9B,8BAA8B;AAC9B,MAAM,CAAC,MAAM,CAAC;IACZ,KAAK,EAAE;QACL,EAAE,EAAE,0DAA0D;KAC/D;CACF,CAAC,CAAC;AA0CH;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAgC,CAAC,EACxD,SAAS,EACT,QAAQ,GAAG,YAAY,EACvB,KAAK,EAAE,aAAa,EACpB,KAAK,GAAG,EAAE,EACV,QAAQ,GAAG,KAAK,EAChB,OAAO,EACP,QAAQ,EACR,OAAO,GAAG,EAAE,EACZ,GAAG,KAAK,EACT,EAAE,EAAE;IACH,2CAA2C;IAC3C,MAAM,EAAC,KAAK,EAAE,QAAQ,EAAC,GAAG,QAAQ,EAAE,CAAC;IAErC,qFAAqF;IACrF,MAAM,KAAK,GACT,aAAa;QACb,CAAC,QAAQ,KAAK,MAAM;YACpB,CAAC,QAAQ,KAAK,QAAQ;gBACpB,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAAC;YAC1D,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,OAAO,CAAC,CAAC;IAEf,MAAM,SAAS,GAAG,MAAM,CAAM,IAAI,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,CAAM,IAAI,CAAC,CAAC;IAEpC,MAAM,oBAAoB,GAAY,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE;QACvD,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;QAC3B,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;QAE3B,0CAA0C;QAC1C,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;YACxB,8DAA8D;YAC9D,MAAM,CAAC,SAAS,CAAC,wBAAwB,CAAC,MAAM,EAAE;gBAChD,SAAS,EAAE;oBACT,IAAI,EAAE;wBACJ,8CAA8C;wBAC9C,CAAC,mBAAmB,EAAE,iBAAiB,CAAC;wBAExC,oEAAoE;wBACpE,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;wBAE1C,wDAAwD;wBACxD,CAAC,kCAAkC,EAAE,QAAQ,CAAC;wBAE9C,WAAW;wBACX,CAAC,yBAAyB,EAAE,SAAS,CAAC;wBAEtC,6BAA6B;wBAC7B,CAAC,WAAW,EAAE,WAAW,CAAC;qBAC3B;iBACF;aACF,CAAC,CAAC;QACL,CAAC;QAED,mDAAmD;QACnD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,gBAAgB,EAAE;YAC1C,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,EAAE;YACT,MAAM,EAAE;gBACN,mBAAmB,EAAE,WAAW,CAAC,cAAc,EAAE,SAAS,CAAC;gBAC3D,mBAAmB,EAAE,WAAW,CAAC,cAAc,EAAE,SAAS,CAAC;gBAC3D,gCAAgC,EAAE,WAAW,CAAC,SAAS,EAAE,SAAS,CAAC;gBACnE,yBAAyB,EAAE,WAAW,CAAC,WAAW,EAAE,SAAS,CAAC;gBAC9D,4BAA4B,EAAE,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC;gBAChE,6BAA6B,EAAE,WAAW,CACxC,oBAAoB,EACpB,SAAS,CACV;gBACD,GAAG,aAAa,CAAC,KAAK,CAAC;aACxB;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE;YACzC,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,EAAE;YACT,MAAM,EAAE;gBACN,mBAAmB,EAAE,WAAW,CAAC,cAAc,EAAE,SAAS,CAAC;gBAC3D,mBAAmB,EAAE,WAAW,CAAC,cAAc,EAAE,SAAS,CAAC;gBAC3D,gCAAgC,EAAE,WAAW,CAAC,SAAS,EAAE,SAAS,CAAC;gBACnE,yBAAyB,EAAE,WAAW,CAAC,WAAW,EAAE,SAAS,CAAC;gBAC9D,4BAA4B,EAAE,WAAW,CAAC,UAAU,EAAE,SAAS,CAAC;gBAChE,6BAA6B,EAAE,WAAW,CACxC,oBAAoB,EACpB,SAAS,CACV;gBACD,GAAG,aAAa,CAAC,IAAI,CAAC;aACvB;SACF,CAAC,CAAC;QAEH,uDAAuD;QACvD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,qBAAqB,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5E,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE1E,+CAA+C;QAC/C,MAAM,MAAM,GACV,QAAQ,KAAK,MAAM;YACnB,CAAC,QAAQ,KAAK,QAAQ;gBACpB,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAAC,CAAC;QAE/D,yCAAyC;QACzC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;YACxB,MAAM,CAAC,MAAM,CAAC,QAAQ,CACpB,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,qBAAqB,CACtD,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC;IAEF,wBAAwB;IACxB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACtB,SAAS,CAAC,OAAO,CAAC,aAAa,CAAC,EAAC,QAAQ,EAAC,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,iDAAiD;IACjD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;YAC7D,MAAM,MAAM,GACV,QAAQ,KAAK,MAAM;gBACnB,CAAC,QAAQ,KAAK,QAAQ;oBACpB,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAAC,CAAC;YAE/D,yCAAyC;YACzC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACxB,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAC/B,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,qBAAqB,CACtD,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAC/B,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,gBAAgB,CAC5C,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;IAExC,wDAAwD;IACxD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,QAAQ,KAAK,QAAQ,IAAI,CAAC,aAAa,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;YACjE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC;YACrE,MAAM,YAAY,GAAG,GAAG,EAAE;gBACxB,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;oBACtB,yCAAyC;oBACzC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;wBACxB,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAC/B,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,qBAAqB,CAClE,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACN,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAC/B,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,gBAAgB,CACxD,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC,CAAC;YAEF,iCAAiC;YACjC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YAEpD,WAAW;YACX,OAAO,GAAG,EAAE;gBACV,UAAU,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACzD,CAAC,CAAC;QACJ,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;IAExC,qCAAqC;IACrC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;IAEtC,MAAM,cAAc,GAAG;QACrB,OAAO,EAAE,EAAC,OAAO,EAAE,KAAK,EAAC;QACzB,oBAAoB,EAAE,KAAK;QAC3B,eAAe,EAAE,IAAI;QACrB,QAAQ;QACR,UAAU;QACV,aAAa,EAAE,IAAI;QACnB,GAAG,OAAO;KACX,CAAC;IAEF,OAAO,CACL,cAAK,SAAS,EAAE,EAAE,CAAC,kBAAkB,EAAE,SAAS,CAAC,YAC/C,KAAC,MAAM,IACL,MAAM,EAAC,MAAM,EACb,KAAK,EAAC,MAAM,EACZ,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,oBAAoB,EAC7B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,KAAC,OAAO,KAAG,KAChB,KAAK,GACT,GACE,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {\n Editor,\n EditorProps,\n OnChange,\n OnMount,\n loader,\n} from '@monaco-editor/react';\nimport {Spinner, cn, useTheme} from '@sqlrooms/ui';\nimport React, {useEffect, useRef} from 'react';\nimport {\n getCssColor,\n getJsonEditorTheme,\n getMenuColors,\n getMonospaceFont,\n} from '../utils/color-utils';\n\n// Configure the Monaco loader\nloader.config({\n paths: {\n vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs',\n },\n});\n\nexport interface MonacoEditorProps extends Omit<EditorProps, 'onMount'> {\n /**\n * Callback when the editor is mounted\n */\n onMount?: OnMount;\n /**\n * Callback when the editor content changes\n */\n onChange?: OnChange;\n /**\n * CSS class name for the editor container\n * @default ''\n */\n className?: string;\n /**\n * The language of the editor\n * @default 'javascript'\n */\n language?: string;\n /**\n * The theme of the editor\n * Can be explicitly set or will automatically use the app theme if not provided\n * @default 'vs-dark'\n */\n theme?: 'vs-dark' | 'light';\n /**\n * The value of the editor\n */\n value?: string;\n /**\n * Whether the editor is read-only\n * @default false\n */\n readOnly?: boolean;\n /**\n * Additional options for the editor\n */\n options?: Record<string, any>;\n}\n\n/**\n * A wrapper around the Monaco Editor component\n */\nexport const MonacoEditor: React.FC<MonacoEditorProps> = ({\n className,\n language = 'javascript',\n theme: explicitTheme,\n value = '',\n readOnly = false,\n onMount,\n onChange,\n options = {},\n ...props\n}) => {\n // Get the app theme from the ThemeProvider\n const {theme: appTheme} = useTheme();\n\n // If a theme is explicitly provided, use it. Otherwise, determine from the app theme\n const theme =\n explicitTheme ||\n (appTheme === 'dark' ||\n (appTheme === 'system' &&\n window.matchMedia('(prefers-color-scheme: dark)').matches)\n ? 'vs-dark'\n : 'light');\n\n const editorRef = useRef<any>(null);\n const monacoRef = useRef<any>(null);\n\n const handleEditorDidMount: OnMount = (editor, monaco) => {\n editorRef.current = editor;\n monacoRef.current = monaco;\n\n // Special language configuration for JSON\n if (language === 'json') {\n // Define a more robust tokenizer for JSON with improved rules\n monaco.languages.setMonarchTokensProvider('json', {\n tokenizer: {\n root: [\n // Property keys (strings followed by a colon)\n [/\"([^\"]*)\"(?=\\s*:)/, 'string.key.json'],\n\n // Regular string values (any quoted string not followed by a colon)\n [/\"([^\"]*)\"(?!\\s*:)/, 'string.value.json'],\n\n // Numbers (integers, decimals, and scientific notation)\n [/-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?/, 'number'],\n\n // Keywords\n [/\\b(?:true|false|null)\\b/, 'keyword'],\n\n // Punctuation and delimiters\n [/[{}[\\],:]/, 'delimiter'],\n ],\n },\n });\n }\n\n // Define editor themes with Tailwind CSS variables\n monaco.editor.defineTheme('sqlrooms-light', {\n base: 'vs',\n inherit: true,\n rules: [],\n colors: {\n 'editor.background': getCssColor('--background', '#ffffff'),\n 'editor.foreground': getCssColor('--foreground', '#000000'),\n 'editor.lineHighlightBackground': getCssColor('--muted', '#f5f5f5'),\n 'editorCursor.foreground': getCssColor('--primary', '#000000'),\n 'editor.selectionBackground': getCssColor('--accent', '#e3e3e3'),\n 'editorLineNumber.foreground': getCssColor(\n '--muted-foreground',\n '#888888',\n ),\n ...getMenuColors(false),\n },\n });\n\n monaco.editor.defineTheme('sqlrooms-dark', {\n base: 'vs-dark',\n inherit: true,\n rules: [],\n colors: {\n 'editor.background': getCssColor('--background', '#1e1e1e'),\n 'editor.foreground': getCssColor('--foreground', '#d4d4d4'),\n 'editor.lineHighlightBackground': getCssColor('--muted', '#2a2a2a'),\n 'editorCursor.foreground': getCssColor('--primary', '#ffffff'),\n 'editor.selectionBackground': getCssColor('--accent', '#264f78'),\n 'editorLineNumber.foreground': getCssColor(\n '--muted-foreground',\n '#858585',\n ),\n ...getMenuColors(true),\n },\n });\n\n // Define JSON-specific themes with rich token coloring\n monaco.editor.defineTheme('sqlrooms-json-light', getJsonEditorTheme(false));\n monaco.editor.defineTheme('sqlrooms-json-dark', getJsonEditorTheme(true));\n\n // Apply the custom theme based on content type\n const isDark =\n appTheme === 'dark' ||\n (appTheme === 'system' &&\n window.matchMedia('(prefers-color-scheme: dark)').matches);\n\n // Use JSON-specific theme for JSON files\n if (language === 'json') {\n monaco.editor.setTheme(\n isDark ? 'sqlrooms-json-dark' : 'sqlrooms-json-light',\n );\n } else {\n monaco.editor.setTheme(isDark ? 'sqlrooms-dark' : 'sqlrooms-light');\n }\n\n if (onMount) {\n onMount(editor, monaco);\n }\n };\n\n // Apply readOnly option\n useEffect(() => {\n if (editorRef.current) {\n editorRef.current.updateOptions({readOnly});\n }\n }, [readOnly]);\n\n // Update the editor theme when app theme changes\n useEffect(() => {\n if (editorRef.current && monacoRef.current && !explicitTheme) {\n const isDark =\n appTheme === 'dark' ||\n (appTheme === 'system' &&\n window.matchMedia('(prefers-color-scheme: dark)').matches);\n\n // Use JSON-specific theme for JSON files\n if (language === 'json') {\n monacoRef.current.editor.setTheme(\n isDark ? 'sqlrooms-json-dark' : 'sqlrooms-json-light',\n );\n } else {\n monacoRef.current.editor.setTheme(\n isDark ? 'sqlrooms-dark' : 'sqlrooms-light',\n );\n }\n }\n }, [appTheme, explicitTheme, language]);\n\n // Listen for system theme changes if using system theme\n useEffect(() => {\n if (appTheme === 'system' && !explicitTheme && monacoRef.current) {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n const handleChange = () => {\n if (monacoRef.current) {\n // Use JSON-specific theme for JSON files\n if (language === 'json') {\n monacoRef.current.editor.setTheme(\n mediaQuery.matches ? 'sqlrooms-json-dark' : 'sqlrooms-json-light',\n );\n } else {\n monacoRef.current.editor.setTheme(\n mediaQuery.matches ? 'sqlrooms-dark' : 'sqlrooms-light',\n );\n }\n }\n };\n\n // Add listener for theme changes\n mediaQuery.addEventListener('change', handleChange);\n\n // Clean up\n return () => {\n mediaQuery.removeEventListener('change', handleChange);\n };\n }\n }, [appTheme, explicitTheme, language]);\n\n // Get monospace font for code editor\n const fontFamily = getMonospaceFont();\n\n const defaultOptions = {\n minimap: {enabled: false},\n scrollBeyondLastLine: false,\n automaticLayout: true,\n readOnly,\n fontFamily,\n fontLigatures: true,\n ...options,\n };\n\n return (\n <div className={cn('w-full h-[300px]', className)}>\n <Editor\n height=\"100%\"\n width=\"100%\"\n language={language}\n theme={theme}\n value={value}\n options={defaultOptions}\n onMount={handleEditorDidMount}\n onChange={onChange}\n loading={<Spinner />}\n {...props}\n />\n </div>\n );\n};\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { MonacoEditorProps } from './MonacoEditor';
|
|
3
|
+
export interface SqlMonacoEditorProps extends Omit<MonacoEditorProps, 'language'> {
|
|
4
|
+
/**
|
|
5
|
+
* Custom SQL keywords to add to the completion provider
|
|
6
|
+
*/
|
|
7
|
+
customKeywords?: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Custom SQL functions to add to the completion provider
|
|
10
|
+
*/
|
|
11
|
+
customFunctions?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Table schemas for autocompletion
|
|
14
|
+
* Format: { tableName: { columnName: columnType, ... }, ... }
|
|
15
|
+
*/
|
|
16
|
+
tableSchemas?: Record<string, Record<string, string>>;
|
|
17
|
+
/**
|
|
18
|
+
* Sample data for tables to enhance autocompletion
|
|
19
|
+
* Format: { tableName: [sampleDescription1, sampleDescription2, ...], ... }
|
|
20
|
+
*/
|
|
21
|
+
tableSamples?: Record<string, string[]>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A Monaco editor for editing SQL with DuckDB syntax highlighting and autocompletion
|
|
25
|
+
*/
|
|
26
|
+
export declare const SqlMonacoEditor: React.FC<SqlMonacoEditorProps>;
|
|
27
|
+
//# sourceMappingURL=SqlMonacoEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqlMonacoEditor.d.ts","sourceRoot":"","sources":["../../src/components/SqlMonacoEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAe,iBAAiB,EAAC,MAAM,gBAAgB,CAAC;AAS/D,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;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA2M1D,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MonacoEditor } from './MonacoEditor';
|
|
3
|
+
import { DUCKDB_KEYWORDS, DUCKDB_FUNCTIONS, SQL_LANGUAGE_CONFIGURATION, } from '../constants/duckdb';
|
|
4
|
+
/**
|
|
5
|
+
* A Monaco editor for editing SQL with DuckDB syntax highlighting and autocompletion
|
|
6
|
+
*/
|
|
7
|
+
export const SqlMonacoEditor = ({ customKeywords = [], customFunctions = [], tableSchemas = {}, tableSamples = {}, onMount, className, ...props }) => {
|
|
8
|
+
// Handle editor mounting to configure SQL language features
|
|
9
|
+
const handleEditorDidMount = (editor, monaco) => {
|
|
10
|
+
// Register SQL language if not already registered
|
|
11
|
+
if (!monaco.languages.getLanguages().some((lang) => lang.id === 'sql')) {
|
|
12
|
+
monaco.languages.register({ id: 'sql' });
|
|
13
|
+
}
|
|
14
|
+
// Combine keywords and functions with custom ones
|
|
15
|
+
const keywords = [...DUCKDB_KEYWORDS, ...customKeywords];
|
|
16
|
+
const functions = [...DUCKDB_FUNCTIONS, ...customFunctions];
|
|
17
|
+
// Set the language configuration
|
|
18
|
+
monaco.languages.setMonarchTokensProvider('sql', {
|
|
19
|
+
...SQL_LANGUAGE_CONFIGURATION,
|
|
20
|
+
keywords,
|
|
21
|
+
builtinFunctions: functions,
|
|
22
|
+
}); // Using 'as any' to bypass the type checking issue
|
|
23
|
+
// Dispose any existing completion providers for SQL
|
|
24
|
+
// Using a safer approach that doesn't rely on internal properties
|
|
25
|
+
try {
|
|
26
|
+
// We'll register our provider and let Monaco handle the disposal of previous ones
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error('Error disposing completion providers:', error);
|
|
30
|
+
}
|
|
31
|
+
// Register SQL completion provider
|
|
32
|
+
const disposable = monaco.languages.registerCompletionItemProvider('sql', {
|
|
33
|
+
triggerCharacters: [' ', '.', ',', '(', '='],
|
|
34
|
+
provideCompletionItems: (model, position) => {
|
|
35
|
+
try {
|
|
36
|
+
const suggestions = [];
|
|
37
|
+
const word = model.getWordUntilPosition(position);
|
|
38
|
+
const range = {
|
|
39
|
+
startLineNumber: position.lineNumber,
|
|
40
|
+
endLineNumber: position.lineNumber,
|
|
41
|
+
startColumn: word.startColumn,
|
|
42
|
+
endColumn: word.endColumn,
|
|
43
|
+
};
|
|
44
|
+
// Get the text before the cursor to determine context
|
|
45
|
+
const lineContent = model.getLineContent(position.lineNumber);
|
|
46
|
+
const textBeforeCursor = lineContent
|
|
47
|
+
.substring(0, position.column - 1)
|
|
48
|
+
.trim()
|
|
49
|
+
.toLowerCase();
|
|
50
|
+
// Check if we're after a FROM, JOIN, or similar clause to prioritize table suggestions
|
|
51
|
+
const isTableContext = /\b(from|join|into|update|table)\s+\w*$/.test(textBeforeCursor);
|
|
52
|
+
// Check if we're after a table name and period to prioritize column suggestions
|
|
53
|
+
const isColumnContext = /\b(\w+)\.\w*$/.test(textBeforeCursor);
|
|
54
|
+
// Add keyword suggestions (if not in a specific context)
|
|
55
|
+
if (!isColumnContext) {
|
|
56
|
+
keywords.forEach((keyword) => {
|
|
57
|
+
suggestions.push({
|
|
58
|
+
label: keyword,
|
|
59
|
+
kind: monaco.languages.CompletionItemKind.Keyword,
|
|
60
|
+
insertText: keyword,
|
|
61
|
+
range: range,
|
|
62
|
+
detail: 'Keyword',
|
|
63
|
+
sortText: isTableContext ? 'z' + keyword : 'a' + keyword, // Lower priority in table context
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Add function suggestions (if not in a specific context)
|
|
68
|
+
if (!isColumnContext) {
|
|
69
|
+
functions.forEach((func) => {
|
|
70
|
+
suggestions.push({
|
|
71
|
+
label: func,
|
|
72
|
+
kind: monaco.languages.CompletionItemKind.Function,
|
|
73
|
+
insertText: func,
|
|
74
|
+
range: range,
|
|
75
|
+
detail: 'Function',
|
|
76
|
+
sortText: isTableContext ? 'z' + func : 'b' + func, // Lower priority in table context
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Add table and column suggestions from schemas
|
|
81
|
+
Object.entries(tableSchemas).forEach(([tableName, columns]) => {
|
|
82
|
+
// Get sample data for this table if available
|
|
83
|
+
const samples = tableSamples[tableName] || [];
|
|
84
|
+
const sampleText = samples.length > 0 ? `\nSample data:\n${samples.join('\n')}` : '';
|
|
85
|
+
// Add table suggestion
|
|
86
|
+
suggestions.push({
|
|
87
|
+
label: tableName,
|
|
88
|
+
kind: monaco.languages.CompletionItemKind.Class,
|
|
89
|
+
insertText: tableName,
|
|
90
|
+
range: range,
|
|
91
|
+
detail: 'Table',
|
|
92
|
+
documentation: {
|
|
93
|
+
value: `Table: ${tableName}${sampleText}`,
|
|
94
|
+
isTrusted: true,
|
|
95
|
+
},
|
|
96
|
+
sortText: isTableContext ? 'a' + tableName : 'c' + tableName, // Higher priority in table context
|
|
97
|
+
});
|
|
98
|
+
// Extract table name from context if we're in a column context
|
|
99
|
+
let contextTableName = '';
|
|
100
|
+
if (isColumnContext) {
|
|
101
|
+
const match = textBeforeCursor.match(/\b(\w+)\.\w*$/);
|
|
102
|
+
if (match && match[1]) {
|
|
103
|
+
contextTableName = match[1];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Only add columns for the current table if we're in a column context
|
|
107
|
+
if (!isColumnContext || contextTableName === tableName) {
|
|
108
|
+
// Add column suggestions
|
|
109
|
+
Object.entries(columns).forEach(([columnName, columnType]) => {
|
|
110
|
+
// Find sample values for this column if available
|
|
111
|
+
const columnSample = samples.find((s) => s.startsWith(`${columnName}:`));
|
|
112
|
+
const sampleInfo = columnSample
|
|
113
|
+
? `\nSample: ${columnSample.split(':')[1]?.trim() || ''}`
|
|
114
|
+
: '';
|
|
115
|
+
suggestions.push({
|
|
116
|
+
label: columnName,
|
|
117
|
+
kind: monaco.languages.CompletionItemKind.Field,
|
|
118
|
+
insertText: columnName,
|
|
119
|
+
range: range,
|
|
120
|
+
detail: `Column (${columnType})`,
|
|
121
|
+
documentation: {
|
|
122
|
+
value: `Column from table ${tableName}${sampleInfo}`,
|
|
123
|
+
isTrusted: true,
|
|
124
|
+
},
|
|
125
|
+
sortText: isColumnContext && contextTableName === tableName
|
|
126
|
+
? 'a' + columnName
|
|
127
|
+
: 'd' + columnName,
|
|
128
|
+
});
|
|
129
|
+
// Only add table.column suggestions if not in a column context
|
|
130
|
+
if (!isColumnContext) {
|
|
131
|
+
suggestions.push({
|
|
132
|
+
label: `${tableName}.${columnName}`,
|
|
133
|
+
kind: monaco.languages.CompletionItemKind.Field,
|
|
134
|
+
insertText: `${tableName}.${columnName}`,
|
|
135
|
+
range: range,
|
|
136
|
+
detail: `Column (${columnType})`,
|
|
137
|
+
documentation: {
|
|
138
|
+
value: `Column from table ${tableName}${sampleInfo}`,
|
|
139
|
+
isTrusted: true,
|
|
140
|
+
},
|
|
141
|
+
sortText: 'e' + tableName + columnName,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
suggestions,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error('Error in SQL completion provider:', error);
|
|
153
|
+
return { suggestions: [] };
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
// Store the disposable to clean up later if needed
|
|
158
|
+
editor.onDidDispose(() => {
|
|
159
|
+
disposable.dispose();
|
|
160
|
+
});
|
|
161
|
+
// Call the original onMount if provided
|
|
162
|
+
if (onMount) {
|
|
163
|
+
onMount(editor, monaco);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
return (_jsx(MonacoEditor, { language: "sql", onMount: handleEditorDidMount, className: className, options: {
|
|
167
|
+
formatOnPaste: true,
|
|
168
|
+
formatOnType: true,
|
|
169
|
+
wordWrap: 'on',
|
|
170
|
+
}, ...props }));
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=SqlMonacoEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqlMonacoEditor.js","sourceRoot":"","sources":["../../src/components/SqlMonacoEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport {MonacoEditor, MonacoEditorProps} from './MonacoEditor';\nimport {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';\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 * Format: { tableName: { columnName: columnType, ... }, ... }\n */\n tableSchemas?: Record<string, Record<string, string>>;\n /**\n * Sample data for tables to enhance autocompletion\n * Format: { tableName: [sampleDescription1, sampleDescription2, ...], ... }\n */\n tableSamples?: Record<string, string[]>;\n}\n\n/**\n * A Monaco editor for editing SQL with DuckDB syntax highlighting and autocompletion\n */\nexport const SqlMonacoEditor: React.FC<SqlMonacoEditorProps> = ({\n customKeywords = [],\n customFunctions = [],\n tableSchemas = {},\n tableSamples = {},\n onMount,\n className,\n ...props\n}) => {\n // Handle editor mounting to configure SQL language features\n const handleEditorDidMount: OnMount = (editor, monaco) => {\n // Register SQL language if not already registered\n if (!monaco.languages.getLanguages().some((lang) => lang.id === 'sql')) {\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 // Dispose any existing completion providers for SQL\n // Using a safer approach that doesn't rely on internal properties\n try {\n // We'll register our provider and let Monaco handle the disposal of previous ones\n } catch (error) {\n console.error('Error disposing completion providers:', error);\n }\n\n // Register SQL completion provider\n const disposable = monaco.languages.registerCompletionItemProvider('sql', {\n triggerCharacters: [' ', '.', ',', '(', '='],\n provideCompletionItems: (model, position) => {\n try {\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 // 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 Object.entries(tableSchemas).forEach(([tableName, columns]) => {\n // Get sample data for this table if available\n const samples = tableSamples[tableName] || [];\n const sampleText =\n samples.length > 0 ? `\\nSample data:\\n${samples.join('\\n')}` : '';\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}${sampleText}`,\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 Object.entries(columns).forEach(([columnName, columnType]) => {\n // Find sample values for this column if available\n const columnSample = samples.find((s) =>\n s.startsWith(`${columnName}:`),\n );\n const sampleInfo = columnSample\n ? `\\nSample: ${columnSample.split(':')[1]?.trim() || ''}`\n : '';\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}${sampleInfo}`,\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}${sampleInfo}`,\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 if needed\n editor.onDidDispose(() => {\n disposable.dispose();\n });\n\n // Call the original onMount if provided\n if (onMount) {\n onMount(editor, monaco);\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"]}
|