diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/ScriptEdit.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/ScriptEdit.razor index f7dd8ab2..0010d172 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/ScriptEdit.razor +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/ScriptEdit.razor @@ -13,7 +13,6 @@ @using ZB.MOM.WW.OtOpcUa.Configuration.Entities @inject IDbContextFactory DbFactory @inject NavigationManager Nav -@inject IJSRuntime JS

@(IsNew ? "New script" : "Edit script")

@@ -57,15 +56,8 @@ else
Source
- @* The textarea stays in the DOM and remains Blazor's source of truth. Monaco - mounts a
beside it (textarea hides), and the loader's onDidChangeModelContent - handler mirrors edits back into the textarea + fires the input event so @bind - picks them up. Falls back to the textarea gracefully if Monaco's CDN is - unreachable (air-gapped deployments — see monaco-loader.js). *@ - -
SHA-256 hash is computed automatically on save. Monaco editor attaches over the textarea on render.
+ +
SHA-256 hash is computed automatically on save.
@@ -110,24 +102,6 @@ else _loaded = true; } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (!firstRender || !_loaded) return; - // Inject loader once, then attach over the textarea. Failures are silent — the page - // is fully usable via the underlying textarea if Monaco's CDN is unreachable. - try - { - await JS.InvokeVoidAsync("eval", "if (!document.querySelector('script[data-otopcua=monaco-loader]')) { var s=document.createElement('script'); s.src='/_content/ZB.MOM.WW.OtOpcUa.AdminUI/js/monaco-loader.js'; s.dataset.otopcua='monaco-loader'; document.head.appendChild(s); }"); - // Wait a tick for the loader IIFE to register window.otOpcUaScriptEditor, then attach. - await Task.Delay(50); - await JS.InvokeVoidAsync("otOpcUaScriptEditor.attach", "script-source"); - } - catch - { - // Textarea remains the editor — no-op. - } - } - private async Task SubmitAsync() { _busy = true; _error = null; diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-loader.js b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-loader.js deleted file mode 100644 index c88822a0..00000000 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/wwwroot/js/monaco-loader.js +++ /dev/null @@ -1,59 +0,0 @@ -// 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 })); - }); - }, - }; -})();