// Phase 7 Stream F — Monaco editor loader for ScriptEditor.razor. // Progressive enhancement: the textarea is authoritative until Monaco attaches; // after attach, Monaco syncs back into the textarea on every change so Blazor's // @bind still sees the latest value. (function () { if (window.otOpcUaScriptEditor) return; const MONACO_CDN = 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs'; let loaderPromise = null; function ensureLoader() { if (loaderPromise) return loaderPromise; loaderPromise = new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = `${MONACO_CDN}/loader.js`; script.onload = () => { window.require.config({ paths: { vs: MONACO_CDN } }); window.require(['vs/editor/editor.main'], () => resolve(window.monaco)); }; script.onerror = () => reject(new Error('Monaco CDN unreachable')); document.head.appendChild(script); }); return loaderPromise; } window.otOpcUaScriptEditor = { attach: async function (textareaId) { const ta = document.getElementById(textareaId); if (!ta) return; const monaco = await ensureLoader(); // Mount Monaco over the textarea. The textarea stays in the DOM as the // source of truth for Blazor's @bind — Monaco mirrors into it on every // keystroke so server-side state stays in sync. const host = document.createElement('div'); host.style.height = '340px'; host.style.border = '1px solid #ced4da'; host.style.borderRadius = '0.25rem'; ta.style.display = 'none'; ta.parentNode.insertBefore(host, ta); const editor = monaco.editor.create(host, { value: ta.value, language: 'csharp', theme: 'vs', automaticLayout: true, fontSize: 13, minimap: { enabled: false }, scrollBeyondLastLine: false, }); editor.onDidChangeModelContent(() => { ta.value = editor.getValue(); ta.dispatchEvent(new Event('input', { bubbles: true })); }); }, }; })();