export class MonacoEditorAdapter { constructor(hostElement) { this.hostElement = hostElement; this.editor = null; this.model = null; this.changeHandler = null; this.suppressChange = false; this.readOnly = true; } async init() { const monaco = await this.#loadMonaco(); if (!monaco || !this.hostElement) return false; this.#defineTheme(monaco); this.model = monaco.editor.createModel("", "plaintext"); this.editor = monaco.editor.create(this.hostElement, { model: this.model, theme: "app-dark-blue", automaticLayout: true, readOnly: this.readOnly, minimap: { enabled: false }, scrollBeyondLastLine: false, fontSize: 14, fontFamily: "IBM Plex Mono, Consolas, monospace", tabSize: 2 }); this.model.onDidChangeContent(() => { if (this.suppressChange || !this.changeHandler) return; this.changeHandler(this.model.getValue()); }); return true; } onChange(handler) { this.changeHandler = handler; } setReadOnly(readOnly) { this.readOnly = Boolean(readOnly); if (!this.editor) return; this.editor.updateOptions({ readOnly: this.readOnly }); } setValue(text) { if (!this.model) return; const next = text || ""; if (this.model.getValue() === next) return; this.suppressChange = true; this.model.pushEditOperations( [], [{ range: this.model.getFullModelRange(), text: next }], () => null ); this.model.pushStackElement(); this.suppressChange = false; } setLanguageByPath(path) { if (!this.model || !window.monaco?.editor) return; window.monaco.editor.setModelLanguage(this.model, this.#detectLanguage(path)); } focus() { if (this.editor) this.editor.focus(); } layout() { if (this.editor) this.editor.layout(); } async #loadMonaco() { if (window.monaco?.editor) return window.monaco; if (typeof window.require !== "function") return null; if (window.__monacoPromise) return window.__monacoPromise; window.__monacoPromise = new Promise((resolve, reject) => { window.require.config({ paths: { vs: "https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs" } }); window.require(["vs/editor/editor.main"], () => resolve(window.monaco), reject); }); return window.__monacoPromise; } #defineTheme(monaco) { monaco.editor.defineTheme("app-dark-blue", { base: "vs-dark", inherit: true, rules: [ { token: "comment", foreground: "7f9bbf" }, { token: "keyword", foreground: "8cc8ff" }, { token: "string", foreground: "a7d98f" } ], colors: { "editor.background": "#0b1830", "editor.foreground": "#dce9ff", "editorLineNumber.foreground": "#5e79a2", "editorLineNumber.activeForeground": "#a7c3eb", "editor.selectionBackground": "#274f8677", "editor.inactiveSelectionBackground": "#274f8644", "editorCursor.foreground": "#4fa0ff" } }); } #detectLanguage(path) { const ext = (path || "").split(".").pop()?.toLowerCase(); switch (ext) { case "js": case "mjs": case "cjs": return "javascript"; case "ts": return "typescript"; case "json": return "json"; case "md": case "markdown": return "markdown"; case "yml": case "yaml": return "yaml"; case "xml": return "xml"; case "html": return "html"; case "css": return "css"; case "sh": case "bash": return "shell"; case "py": return "python"; case "toml": return "ini"; default: return "plaintext"; } } }