diff --git a/src/components/core-editor-lsp.tsx b/src/components/core-editor-lsp.tsx
new file mode 100644
index 0000000..b2205da
--- /dev/null
+++ b/src/components/core-editor-lsp.tsx
@@ -0,0 +1,118 @@
+"use client";
+
+import dynamic from "next/dynamic";
+import { Skeleton } from "./ui/skeleton";
+import { highlighter } from "@/lib/shiki";
+import type { editor } from "monaco-editor";
+import { shikiToMonaco } from "@shikijs/monaco";
+import type { Monaco } from "@monaco-editor/react";
+import { useCallback, useEffect, useRef } from "react";
+import { connectToLanguageServer } from "@/lib/language-server";
+import { LanguageServerMetadata } from "@/types/language-server";
+import type { MonacoLanguageClient } from "monaco-languageclient";
+
+const Editor = dynamic(
+ async () => {
+ await import("vscode");
+ const monaco = await import("monaco-editor");
+ const { loader } = await import("@monaco-editor/react");
+ loader.config({ monaco });
+ return (await import("@monaco-editor/react")).Editor;
+ },
+ {
+ ssr: false,
+ loading: () =>
+ }
+);
+
+interface CoreEditorLspProps {
+ language?: string;
+ theme?: string;
+ path?: string;
+ value?: string;
+ className?: string;
+ lspConfig?: LanguageServerMetadata;
+ editorConfig?: editor.IEditorConstructionOptions;
+ enableLSP?: boolean;
+}
+
+export default function CoreEditorLsp({
+ language,
+ theme,
+ path,
+ value,
+ className,
+ lspConfig,
+ editorConfig,
+ enableLSP = true,
+}: CoreEditorLspProps) {
+ const editorRef = useRef(null);
+ const monacoLanguageClientRef = useRef(null);
+
+ // Connect to LSP only if enabled
+ const connectLSP = useCallback(async () => {
+ if (!enableLSP || !language || !lspConfig || !editorRef.current) return;
+
+ // If there's an existing language client, stop it first
+ if (monacoLanguageClientRef.current) {
+ monacoLanguageClientRef.current.stop();
+ monacoLanguageClientRef.current = null;
+ }
+
+ // Create a new language client
+ try {
+ const monacoLanguageClient = await connectToLanguageServer(
+ lspConfig.protocol,
+ lspConfig.hostname,
+ lspConfig.port,
+ lspConfig.path,
+ lspConfig.lang
+ );
+ monacoLanguageClientRef.current = monacoLanguageClient;
+ } catch (error) {
+ console.error("Failed to connect to LSP:", error);
+ }
+ }, [language, lspConfig, enableLSP]);
+
+ // Connect to LSP once the editor has mounted
+ const handleEditorDidMount = useCallback(
+ async (editor: editor.IStandaloneCodeEditor) => {
+ editorRef.current = editor;
+ await connectLSP();
+ },
+ [connectLSP]
+ );
+
+ // Reconnect to the LSP whenever language or lspConfig changes
+ useEffect(() => {
+ connectLSP();
+ }, [lspConfig, language, connectLSP]);
+
+ // Cleanup the LSP connection when the component unmounts
+ useEffect(() => {
+ return () => {
+ if (monacoLanguageClientRef.current) {
+ monacoLanguageClientRef.current.stop();
+ monacoLanguageClientRef.current = null;
+ }
+ };
+ }, []);
+
+ function handleEditorWillMount(monaco: Monaco) {
+ shikiToMonaco(highlighter, monaco);
+ }
+
+ return (
+ }
+ />
+ );
+}