Initial commit: ClawdDotNet
Import des bestehenden Projektstands in Git. - .NET 10 WinForms Anwendung (Multi-Agent / Tool-System) - .gitignore fuer Build-Artefakte, Secrets und Runtime-Daten ergaenzt Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ClawdDotNet.UI;
|
||||
|
||||
public sealed record BridgeMessage(
|
||||
[property: JsonPropertyName("type")] string Type,
|
||||
[property: JsonPropertyName("agentId")] string? AgentId = null,
|
||||
[property: JsonPropertyName("content")] string? Content = null,
|
||||
[property: JsonPropertyName("status")] string? Status = null,
|
||||
[property: JsonPropertyName("stepCount")] int? StepCount = null,
|
||||
[property: JsonPropertyName("tokenCount")] int? TokenCount = null,
|
||||
[property: JsonPropertyName("error")] string? Error = null,
|
||||
[property: JsonPropertyName("extra")] object? Extra = null
|
||||
);
|
||||
|
||||
public static class BridgeTypes
|
||||
{
|
||||
// C# → Browser
|
||||
public const string AgentListUpdate = "agent_list_update";
|
||||
public const string AgentStatusUpdate = "agent_status";
|
||||
public const string SelectAgent = "select_agent";
|
||||
public const string ChatMessage = "chat_message";
|
||||
public const string ChatTyping = "chat_typing";
|
||||
public const string ChatHistory = "chat_history";
|
||||
public const string RunStarted = "run_started";
|
||||
public const string RunFinished = "run_finished";
|
||||
|
||||
// Browser → C#
|
||||
public const string UserMessage = "user_message";
|
||||
public const string OpenAgentChat = "open_agent_chat";
|
||||
public const string RunNow = "run_now";
|
||||
public const string AbortRun = "abort_run";
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace ClawdDotNet.UI;
|
||||
|
||||
public static class EmbeddedUiManager
|
||||
{
|
||||
private static string? _extractedPath;
|
||||
|
||||
public static string ExtractToTemp()
|
||||
{
|
||||
if (_extractedPath is not null) return _extractedPath;
|
||||
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), "ClawdDotNet_UI",
|
||||
Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "dev");
|
||||
|
||||
Directory.CreateDirectory(tempDir);
|
||||
|
||||
var asm = Assembly.GetExecutingAssembly();
|
||||
var prefix = "ClawdDotNet.EmbeddedUI.";
|
||||
|
||||
foreach (var name in asm.GetManifestResourceNames()
|
||||
.Where(n => n.StartsWith(prefix)))
|
||||
{
|
||||
// "ClawdDotNet.EmbeddedUI.chat.html" → "chat.html"
|
||||
var fileName = name[prefix.Length..];
|
||||
var dest = Path.Combine(tempDir, fileName);
|
||||
|
||||
using var stream = asm.GetManifestResourceStream(name)!;
|
||||
using var file = File.Create(dest);
|
||||
stream.CopyTo(file);
|
||||
}
|
||||
|
||||
_extractedPath = tempDir;
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
public static string GetExtractedPath()
|
||||
=> _extractedPath ?? throw new InvalidOperationException(
|
||||
"EmbeddedUiManager.ExtractToTemp() must be called first.");
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
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<BridgeMessage>? 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<Task>(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<BridgeMessage>(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user