Expand settings and logs agent tools
PR and Push Build/Test / build-and-test (push) Successful in 9m36s

This commit is contained in:
2026-06-01 21:07:08 +02:00
parent abac46c691
commit 11c65ce669
12 changed files with 1160 additions and 45 deletions
@@ -1,4 +1,6 @@
#if WINDOWS
using System.Runtime.InteropServices;
using System.Text;
using Aprillz.MewUI;
using Aprillz.MewUI.Controls;
@@ -6,6 +8,8 @@ namespace MeetingAssistant.Workflow;
public sealed class MewUiWorkflowRulesEditorWindowService : IWorkflowRulesEditorWindowService
{
internal const string WindowTitle = "Settings and logs";
private readonly IServiceProvider services;
private readonly ILogger<MewUiWorkflowRulesEditorWindowService> logger;
private readonly object sync = new();
@@ -26,7 +30,13 @@ public sealed class MewUiWorkflowRulesEditorWindowService : IWorkflowRulesEditor
if (isRunning)
{
logger.LogInformation("Workflow rules editor UI is already running");
return;
if (WorkflowRulesEditorWindowActivation.TryActivate(WindowTitle))
{
return;
}
logger.LogWarning("Workflow rules editor UI was marked running, but no window could be activated; starting a new UI thread");
isRunning = false;
}
isRunning = true;
@@ -108,7 +118,7 @@ internal sealed class WorkflowRulesEditorMewWindow
.OnClick(() => _ = SendAsync());
var window = new Window()
.Title("Settings and logs")
.Title(MewUiWorkflowRulesEditorWindowService.WindowTitle)
.Resizable(680, 760, 520, 460)
.Padding(0)
.OnLoaded(Render)
@@ -238,14 +248,140 @@ internal sealed class WorkflowRulesEditorMewWindow
private static Element CreateMessageCard(WorkflowRulesEditorChatMessage message)
{
var isUser = message.Role == WorkflowRulesEditorChatRole.User;
return new Border()
var copyButton = new Button()
.Content("⧉")
.Width(28)
.Height(24)
.ToolTip("Copy message")
.OnClick(() => WorkflowRulesEditorClipboard.SetText(message.Content));
copyButton.IsVisible = false;
var copyRow = new DockPanel()
.LastChildFill(false)
.Children(copyButton.DockRight());
var cardContent = new StackPanel()
.Vertical()
.Spacing(4)
.Children(
copyRow,
WorkflowRulesEditorMarkdownRenderer.CreateContent(message.Content));
var card = new Border()
.Padding(10)
.Margin(isUser ? new Thickness(42, 0, 4, 0) : new Thickness(0, 0, 42, 0))
.CornerRadius(8)
.BorderThickness(1)
.BorderBrush(isUser ? Color.FromRgb(68, 95, 132) : Color.FromRgb(69, 75, 86))
.Background(isUser ? Color.FromRgb(26, 48, 78) : Color.FromRgb(35, 39, 46))
.Child(WorkflowRulesEditorMarkdownRenderer.CreateContent(message.Content));
.OnMouseEnter(() => copyButton.IsVisible = true)
.OnMouseLeave(() => copyButton.IsVisible = false)
.Child(cardContent);
return card;
}
}
internal static class WorkflowRulesEditorWindowActivation
{
private const int SwRestore = 9;
public static bool TryActivate(string title)
{
if (string.IsNullOrWhiteSpace(title))
{
return false;
}
var handle = FindWindow(null, title);
if (handle == IntPtr.Zero)
{
return false;
}
ShowWindow(handle, SwRestore);
return SetForegroundWindow(handle);
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindow(string? lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
}
internal static class WorkflowRulesEditorClipboard
{
private const uint CfUnicodeText = 13;
private const uint GmemMoveable = 0x0002;
private const uint GmemZeroInit = 0x0040;
public static void SetText(string text)
{
var bytes = Encoding.Unicode.GetBytes((text ?? "") + '\0');
var handle = GlobalAlloc(GmemMoveable | GmemZeroInit, (UIntPtr)bytes.Length);
if (handle == IntPtr.Zero)
{
return;
}
var locked = GlobalLock(handle);
if (locked == IntPtr.Zero)
{
GlobalFree(handle);
return;
}
Marshal.Copy(bytes, 0, locked, bytes.Length);
GlobalUnlock(handle);
if (!OpenClipboard(IntPtr.Zero))
{
GlobalFree(handle);
return;
}
try
{
EmptyClipboard();
if (SetClipboardData(CfUnicodeText, handle) != IntPtr.Zero)
{
handle = IntPtr.Zero;
}
}
finally
{
CloseClipboard();
if (handle != IntPtr.Zero)
{
GlobalFree(handle);
}
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool EmptyClipboard();
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool CloseClipboard();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalFree(IntPtr hMem);
}
#endif