import { useCallback, useMemo } from "react"; import { LogLevel } from "@codingame/monaco-vscode-api"; import { MonacoEditorReactComp, } from "@typefox/monaco-editor-react"; import type { MonacoVscodeApiConfig } from "monaco-languageclient/vscodeApiWrapper"; import type { EditorAppConfig, TextContents } from "monaco-languageclient/editorApp"; import type { LanguageClientConfig } from "monaco-languageclient/lcwrapper"; import { configureDefaultWorkerFactory } from "monaco-languageclient/workerFactory"; import "../../nwscript-extension/index.js"; function getVscodeApiConfig(): MonacoVscodeApiConfig { return { $type: "extended", viewsConfig: { $type: "EditorService", }, userConfiguration: { json: JSON.stringify({ "workbench.colorTheme": "Default Dark Modern", "workbench.colorCustomizations": { "editor.background": "#231e17", "editor.foreground": "#ece8e3", "editor.lineHighlightBackground": "#302a2040", "editor.selectionBackground": "#3d3018", "editor.selectionHighlightBackground": "#3d301860", "editor.inactiveSelectionBackground": "#3d301850", "editor.findMatchBackground": "#b07a0040", "editor.findMatchHighlightBackground": "#b07a0025", "editor.hoverHighlightBackground": "#3d301830", "editorCursor.foreground": "#b07a00", "editorWhitespace.foreground": "#4a403550", "editorIndentGuide.background": "#4a403530", "editorIndentGuide.activeBackground": "#4a403580", "editorLineNumber.foreground": "#a69f9650", "editorLineNumber.activeForeground": "#ece8e3", "editorBracketMatch.background": "#3d301840", "editorBracketMatch.border": "#b07a0080", "editorOverviewRuler.border": "#4a4035", "editorGutter.background": "#231e17", "editorWidget.background": "#3b3328", "editorWidget.foreground": "#ece8e3", "editorWidget.border": "#4a4035", "editorSuggestWidget.background": "#3b3328", "editorSuggestWidget.border": "#4a4035", "editorSuggestWidget.foreground": "#ece8e3", "editorSuggestWidget.highlightForeground": "#b07a00", "editorSuggestWidget.selectedBackground": "#3d3018", "editorHoverWidget.background": "#3b3328", "editorHoverWidget.border": "#4a4035", "editorGroupHeader.tabsBackground": "#302a20", "editorGroupHeader.tabsBorder": "#4a4035", "editorGroup.border": "#4a4035", "tab.activeBackground": "#231e17", "tab.activeForeground": "#ece8e3", "tab.activeBorderTop": "#b07a00", "tab.inactiveBackground": "#302a20", "tab.inactiveForeground": "#a69f96", "tab.border": "#4a4035", "input.background": "#231e17", "input.foreground": "#ece8e3", "input.border": "#4a4035", "input.placeholderForeground": "#a69f9680", "inputOption.activeBorder": "#b07a00", "dropdown.background": "#3b3328", "dropdown.foreground": "#ece8e3", "dropdown.border": "#4a4035", "list.activeSelectionBackground": "#3d3018", "list.activeSelectionForeground": "#ece8e3", "list.inactiveSelectionBackground": "#3d301880", "list.hoverBackground": "#302a2080", "list.highlightForeground": "#b07a00", "scrollbarSlider.background": "#4a403540", "scrollbarSlider.hoverBackground": "#4a403580", "scrollbarSlider.activeBackground": "#4a4035a0", "focusBorder": "#b07a0080", "peekView.border": "#b07a00", "peekViewEditor.background": "#231e17", "peekViewResult.background": "#302a20", "peekViewTitle.background": "#302a20", "minimap.selectionHighlight": "#3d3018", "minimap.findMatchHighlight": "#b07a0060", }, "editor.fontSize": 14, "editor.fontFamily": "'JetBrains Mono Variable', 'Fira Code', monospace", "editor.tabSize": 4, "editor.insertSpaces": true, "editor.minimap.enabled": false, "editor.scrollBeyondLastLine": false, "editor.wordWrap": "off", "editor.renderWhitespace": "selection", "editor.bracketPairColorization.enabled": true, "editor.padding.top": 8, "editor.lineNumbers": "on", "editor.guides.bracketPairsHorizontal": "active", "editor.wordBasedSuggestions": "off", "editor.quickSuggestions": true, "editor.parameterHints.enabled": true, }), }, monacoWorkerFactory: configureDefaultWorkerFactory, }; } function languageFromPath(filePath: string): string { const ext = filePath.split(".").pop()?.toLowerCase(); const map: Record = { nss: "nwscript", json: "json", sql: "sql", css: "css", html: "html", xml: "xml", js: "javascript", ts: "typescript", md: "markdown", txt: "plaintext", "2da": "plaintext", cfg: "ini", sh: "shellscript", }; return map[ext ?? ""] ?? "plaintext"; } interface MonacoEditorProps { filePath: string; content: string; language?: string; onChange?: (value: string) => void; workspacePath?: string; } export function MonacoEditor({ filePath, content, language, onChange, workspacePath, }: MonacoEditorProps) { const resolvedLang = language ?? languageFromPath(filePath); const isNwscript = resolvedLang === "nwscript"; const fileUri = workspacePath ? `file://${workspacePath}/repos/nwn-module/${filePath}` : `file:///workspace/repos/nwn-module/${filePath}`; const editorAppConfig = useMemo( () => ({ codeResources: { modified: { text: content, uri: fileUri, enforceLanguageId: resolvedLang, }, }, editorOptions: { automaticLayout: true, }, }), [filePath], ); const languageClientConfig = useMemo(() => { if (!isNwscript) return undefined; const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; return { languageId: "nwscript", connection: { options: { $type: "WebSocketUrl" as const, url: `${protocol}//${window.location.host}/ws/lsp`, }, }, clientOptions: { documentSelector: ["nwscript"], workspaceFolder: workspacePath ? { index: 0, name: "nwn-module", uri: { scheme: "file", path: `${workspacePath}/repos/nwn-module` } as any, } : undefined, }, }; }, [isNwscript, workspacePath]); const handleTextChanged = useCallback( (textChanges: TextContents) => { if (textChanges.modified !== undefined) { onChange?.(textChanges.modified); } }, [onChange], ); const handleError = useCallback((error: Error) => { console.error("[MonacoEditor]", error.message, error.stack); }, []); return ( ); }