open-webui/src/lib/components/common/CodeEditor.svelte

137 lines
3.4 KiB
Svelte
Raw Normal View History

2024-06-10 22:24:26 +00:00
<script lang="ts">
import { basicSetup, EditorView } from 'codemirror';
import { keymap, placeholder } from '@codemirror/view';
2024-06-10 23:02:23 +00:00
import { Compartment, EditorState } from '@codemirror/state';
2024-06-10 22:24:26 +00:00
import { acceptCompletion } from '@codemirror/autocomplete';
import { indentWithTab } from '@codemirror/commands';
2024-06-11 00:12:48 +00:00
import { indentUnit } from '@codemirror/language';
2024-06-10 22:24:26 +00:00
import { python } from '@codemirror/lang-python';
import { oneDark } from '@codemirror/theme-one-dark';
2024-06-24 19:23:12 +00:00
import { onMount, createEventDispatcher, getContext } from 'svelte';
2024-06-11 00:12:48 +00:00
import { formatPythonCode } from '$lib/apis/utils';
import { toast } from 'svelte-sonner';
const dispatch = createEventDispatcher();
2024-06-24 19:26:07 +00:00
const i18n = getContext('i18n');
2024-06-10 22:24:26 +00:00
2024-06-10 23:35:42 +00:00
export let boilerplate = '';
2024-06-10 22:24:26 +00:00
export let value = '';
2024-06-10 23:02:23 +00:00
let codeEditor;
let isDarkMode = false;
let editorTheme = new Compartment();
2024-06-11 00:30:07 +00:00
export const formatPythonCodeHandler = async () => {
2024-06-11 00:12:48 +00:00
if (codeEditor) {
const res = await formatPythonCode(value).catch((error) => {
toast.error(error);
return null;
});
if (res && res.code) {
const formattedCode = res.code;
codeEditor.dispatch({
changes: [{ from: 0, to: codeEditor.state.doc.length, insert: formattedCode }]
});
2024-06-11 00:24:42 +00:00
2024-06-24 16:09:45 +00:00
toast.success($i18n.t('Code formatted successfully'));
2024-06-11 00:12:48 +00:00
return true;
}
return false;
}
2024-06-11 02:19:53 +00:00
return false;
2024-06-11 00:12:48 +00:00
};
2024-06-10 23:02:23 +00:00
let extensions = [
basicSetup,
keymap.of([{ key: 'Tab', run: acceptCompletion }, indentWithTab]),
python(),
2024-06-11 00:12:48 +00:00
indentUnit.of(' '),
2024-06-10 23:02:23 +00:00
placeholder('Enter your code here...'),
EditorView.updateListener.of((e) => {
if (e.docChanged) {
value = e.state.doc.toString();
}
}),
editorTheme.of([])
];
2024-06-10 22:24:26 +00:00
onMount(() => {
2024-06-11 04:53:51 +00:00
console.log(value);
if (value === '') {
value = boilerplate;
}
2024-06-11 00:24:42 +00:00
2024-06-10 23:02:23 +00:00
// Check if html class has dark mode
isDarkMode = document.documentElement.classList.contains('dark');
2024-06-10 22:24:26 +00:00
// python code editor, highlight python code
2024-06-10 23:02:23 +00:00
codeEditor = new EditorView({
2024-06-10 22:24:26 +00:00
state: EditorState.create({
2024-06-11 00:24:42 +00:00
doc: value,
2024-06-10 23:02:23 +00:00
extensions: extensions
2024-06-10 22:24:26 +00:00
}),
parent: document.getElementById('code-textarea')
});
2024-06-10 23:02:23 +00:00
if (isDarkMode) {
codeEditor.dispatch({
effects: editorTheme.reconfigure(oneDark)
});
}
// listen to html class changes this should fire only when dark mode is toggled
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const _isDarkMode = document.documentElement.classList.contains('dark');
if (_isDarkMode !== isDarkMode) {
isDarkMode = _isDarkMode;
if (_isDarkMode) {
codeEditor.dispatch({
effects: editorTheme.reconfigure(oneDark)
});
} else {
codeEditor.dispatch({
effects: editorTheme.reconfigure()
});
}
}
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
2024-06-11 05:33:25 +00:00
const keydownHandler = async (e) => {
2024-06-11 00:12:48 +00:00
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
2024-06-11 03:58:47 +00:00
dispatch('save');
2024-06-11 00:12:48 +00:00
}
2024-06-11 05:33:25 +00:00
// Format code when Ctrl + Shift + F is pressed
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'f') {
e.preventDefault();
await formatPythonCodeHandler();
}
2024-06-11 00:12:48 +00:00
};
2024-06-11 05:33:25 +00:00
document.addEventListener('keydown', keydownHandler);
2024-06-11 00:12:48 +00:00
2024-06-10 23:02:23 +00:00
return () => {
observer.disconnect();
2024-06-11 05:33:25 +00:00
document.removeEventListener('keydown', keydownHandler);
2024-06-10 23:02:23 +00:00
};
2024-06-10 22:24:26 +00:00
});
</script>
2024-06-10 23:02:23 +00:00
<div id="code-textarea" class="h-full w-full" />