waveterm/frontend/app/view/codeeditor/codeeditor.tsx
Evan Simkowitz 6413d49119
Make default monaco theme transparent, remove import errors (#308)
This makes the background for the "wave-theme-dark" theme transparent.
The light theme is still opaque because otherwise it will look somewhat
dark.

This also suppresses TypeScript/JavaScript import errors in the default
linter, since we don't have support for project directories.

This also reworks the useWidth and useHeight hooks to use the
useResizeObserver hook, which limits the number of ResizeObserver
instances floating around, thereby improving performance
2024-09-03 18:43:59 -07:00

124 lines
3.8 KiB
TypeScript

// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { useHeight } from "@/app/hook/useHeight";
import { useWidth } from "@/app/hook/useWidth";
import loader from "@monaco-editor/loader";
import { Editor, Monaco } from "@monaco-editor/react";
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
import React, { useRef } from "react";
import "./codeeditor.less";
// there is a global monaco variable (TODO get the correct TS type)
declare var monaco: Monaco;
export function loadMonaco() {
loader.config({ paths: { vs: "monaco" } });
loader
.init()
.then(() => {
monaco.editor.defineTheme("wave-theme-dark", {
base: "vs-dark",
inherit: true,
rules: [],
colors: {
"editor.background": "#00000000",
"minimap.background": "#00000077",
focusBorder: "#00000000",
},
});
monaco.editor.defineTheme("wave-theme-light", {
base: "vs",
inherit: true,
rules: [],
colors: {
"editor.background": "#fefefe",
focusBorder: "#00000000",
},
});
// Disable default validation errors for typescript and javascript
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
});
})
.catch((e) => {
console.error("error loading monaco", e);
});
}
function defaultEditorOptions(): MonacoTypes.editor.IEditorOptions {
const opts: MonacoTypes.editor.IEditorOptions = {
scrollBeyondLastLine: false,
fontSize: 12,
fontFamily: "Hack",
smoothScrolling: true,
scrollbar: {
useShadows: false,
verticalScrollbarSize: 5,
horizontalScrollbarSize: 5,
},
minimap: {
enabled: true,
},
};
return opts;
}
interface CodeEditorProps {
parentRef: React.MutableRefObject<HTMLDivElement>;
text: string;
filename: string;
language?: string;
onChange?: (text: string) => void;
onMount?: (monacoPtr: MonacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco) => () => void;
}
export function CodeEditor({ parentRef, text, language, filename, onChange, onMount }: CodeEditorProps) {
const divRef = useRef<HTMLDivElement>(null);
const unmountRef = useRef<() => void>(null);
const parentHeight = useHeight(parentRef);
const parentWidth = useWidth(parentRef);
const theme = "wave-theme-dark";
React.useEffect(() => {
return () => {
// unmount function
if (unmountRef.current) {
unmountRef.current();
}
};
}, []);
function handleEditorChange(text: string, ev: MonacoTypes.editor.IModelContentChangedEvent) {
if (onChange) {
onChange(text);
}
}
function handleEditorOnMount(editor: MonacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco) {
if (onMount) {
unmountRef.current = onMount(editor, monaco);
}
}
const editorOpts = defaultEditorOptions();
return (
<div className="code-editor-wrapper">
<div className="code-editor" ref={divRef}>
<Editor
theme={theme}
value={text}
options={editorOpts}
height={parentHeight}
width={parentWidth}
onChange={handleEditorChange}
onMount={handleEditorOnMount}
path={filename}
language={language}
/>
</div>
</div>
);
}