@namespace ScadaLink.CentralUI.Components.Shared @implements IAsyncDisposable @inject IJSRuntime JS @if (ShowToolbar) {
}
@code { [Parameter] public string Value { get; set; } = ""; [Parameter] public EventCallback ValueChanged { get; set; } [Parameter] public string Language { get; set; } = "csharp"; [Parameter] public string Height { get; set; } = "320px"; [Parameter] public bool ReadOnly { get; set; } = false; [Parameter] public bool ShowToolbar { get; set; } = true; /// /// Runtime globals surface the script is analyzed against. Defaults to /// template/shared-script globals; set to InboundApi on the API /// method editor so Route and Parameters type-check. /// [Parameter] public ScriptAnalysis.ScriptKind ScriptKind { get; set; } = ScriptAnalysis.ScriptKind.Template; /// /// Parameter names declared on the form (derived from the SchemaBuilder's /// JSON Schema), surfaced as completions inside Parameters["..."] literals /// and used by the unknown-key diagnostic. /// [Parameter] public IReadOnlyList? DeclaredParameters { get; set; } /// /// Full shapes (name + type + required) for the declared parameters. /// Used by Parameters["name"] hover to show the declared type. If null, /// derived from with type "Object". /// [Parameter] public IReadOnlyList? DeclaredParameterShapes { get; set; } /// /// Shapes (name + parameter list + return type) of other scripts on the /// same template. Surfaced inside CallScript("...") for completion, /// signature help, hover, and argument-count diagnostics. /// [Parameter] public IReadOnlyList? SiblingScripts { get; set; } /// /// Attributes declared on the current template. Surfaced inside /// Attributes["..."] for completion and SCADA006 diagnostics. /// [Parameter] public IReadOnlyList? SelfAttributes { get; set; } /// /// Child compositions on the current template, each with its template's /// attributes and scripts. Surfaced for Children["X"].Attributes, /// Children["X"].CallScript, and SCADA007 diagnostics. /// [Parameter] public IReadOnlyList? Children { get; set; } /// /// Parent template when the current template is composed inside exactly /// one other template. null at the root or when multiple parents /// exist. Surfaced for Parent.Attributes / Parent.CallScript. /// [Parameter] public ScriptAnalysis.CompositionContext? Parent { get; set; } /// /// Fires whenever Monaco's marker set updates (after the 500 ms diagnostic /// debounce). Hosts can render a with the same /// data. /// [Parameter] public EventCallback> MarkersChanged { get; set; } private ElementReference _hostRef; private DotNetObjectReference? _dotNetRef; private readonly string _id = Guid.NewGuid().ToString("N"); private string _lastSentValue = ""; private bool _initialized; private bool _wrap; private bool _minimap; private bool _dark; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { _dotNetRef = DotNetObjectReference.Create(this); _lastSentValue = Value ?? ""; try { await JS.InvokeVoidAsync( "MonacoBlazor.createEditor", _id, _hostRef, new { value = Value ?? "", language = Language, readOnly = ReadOnly }, _dotNetRef); _initialized = true; } catch { // Prerendering or JS not ready — swallow; subsequent render will retry. } } else if (_initialized && (Value ?? "") != _lastSentValue) { _lastSentValue = Value ?? ""; try { await JS.InvokeVoidAsync("MonacoBlazor.setValue", _id, _lastSentValue); } catch { } } } [JSInvokable] public Task OnValueChanged(string newValue) { _lastSentValue = newValue ?? ""; return ValueChanged.InvokeAsync(_lastSentValue); } [JSInvokable] public Task OnMarkersChanged(ScriptAnalysis.DiagnosticMarker[] markers) => MarkersChanged.InvokeAsync(markers ?? Array.Empty()); /// Programmatic scroll-to-line (called by the problems panel). public async Task RevealLineAsync(int line, int column = 1) { if (!_initialized) return; try { await JS.InvokeVoidAsync("MonacoBlazor.revealLine", _id, line, column); } catch { } } /// /// Called from JS at completion-request time so the form's latest state is /// passed through, not whatever was captured when the editor was created. /// [JSInvokable] public ScadaContext GetContext() => new( DeclaredParameters?.ToArray() ?? Array.Empty(), SiblingScripts?.ToArray() ?? Array.Empty(), DeclaredParameterShapes?.ToArray() ?? DeclaredParameters?.Select(n => new ScriptAnalysis.ParameterShape(n, "Object", true)).ToArray() ?? Array.Empty(), SelfAttributes?.ToArray() ?? Array.Empty(), Children?.ToArray() ?? Array.Empty(), Parent, ScriptKind); private async Task FormatAsync() { if (!_initialized) return; try { await JS.InvokeVoidAsync("MonacoBlazor.format", _id); } catch { } } private async Task ToggleWrap() { _wrap = !_wrap; try { await JS.InvokeVoidAsync("MonacoBlazor.setEditorOption", _id, "wordWrap", _wrap ? "on" : "off"); } catch { } } private async Task ToggleMinimap() { _minimap = !_minimap; try { await JS.InvokeVoidAsync("MonacoBlazor.setEditorOption", _id, "minimap", new { enabled = _minimap }); } catch { } } private async Task ToggleTheme() { _dark = !_dark; try { await JS.InvokeVoidAsync("MonacoBlazor.setEditorOption", _id, "theme", _dark ? "vs-dark" : "vs"); } catch { } } public async ValueTask DisposeAsync() { if (_initialized) { try { await JS.InvokeVoidAsync("MonacoBlazor.dispose", _id); } catch { } } _dotNetRef?.Dispose(); } public record ScadaContext( string[] DeclaredParameters, ScriptAnalysis.ScriptShape[] SiblingScripts, ScriptAnalysis.ParameterShape[] DeclaredParameterShapes, ScriptAnalysis.AttributeShape[] SelfAttributes, ScriptAnalysis.CompositionContext[] Children, ScriptAnalysis.CompositionContext? Parent, ScriptAnalysis.ScriptKind ScriptKind); }