using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Extensions.Logging; using Microsoft.Web.WebView2.Core; namespace ClawdDotNet.UI; public sealed class WebViewBridge : IDisposable { private static readonly JsonSerializerOptions JsonOpts = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; private readonly Microsoft.Web.WebView2.WinForms.WebView2 _wv; private readonly ILogger _logger; public event Action? MessageReceived; public WebViewBridge( Microsoft.Web.WebView2.WinForms.WebView2 webView, ILogger logger) { _wv = webView; _logger = logger; _wv.CoreWebView2.WebMessageReceived += OnWebMessageReceived; } public async Task SendAsync(BridgeMessage message) { var json = JsonSerializer.Serialize(message, JsonOpts); var script = $"window.__bridge?.receive({json})"; if (_wv.InvokeRequired) { await Task.Factory.FromAsync( _wv.BeginInvoke(new Func(async () => await _wv.CoreWebView2.ExecuteScriptAsync(script))), _ => { }); } else { await _wv.CoreWebView2.ExecuteScriptAsync(script); } } private void OnWebMessageReceived(object? sender, CoreWebView2WebMessageReceivedEventArgs e) { try { var raw = e.TryGetWebMessageAsString(); _logger.LogDebug("Bridge raw incoming: {Raw}", raw); if (string.IsNullOrWhiteSpace(raw)) return; var msg = JsonSerializer.Deserialize(raw, JsonOpts); _logger.LogInformation("Bridge received: type={Type}, agentId={AgentId}, content={Content}", msg?.Type, msg?.AgentId, msg?.Content?.Length > 50 ? msg.Content[..50] + "..." : msg?.Content); if (msg is not null) MessageReceived?.Invoke(msg); } catch (Exception ex) { _logger.LogError(ex, "Bridge: failed to deserialize incoming message"); } } public void Dispose() { if (_wv.CoreWebView2 is not null) _wv.CoreWebView2.WebMessageReceived -= OnWebMessageReceived; } }