mirror of
https://github.com/tuxdotrs/highminded.git
synced 2025-08-23 08:01:03 +05:30
feat: screen aware answers
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using System;
|
using System;
|
||||||
using System.ClientModel;
|
using System.ClientModel;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using highminded.utils;
|
using highminded.utils;
|
||||||
using OpenAI.Chat;
|
using OpenAI.Chat;
|
||||||
using OpenAI;
|
|
||||||
using Markdig;
|
using Markdig;
|
||||||
using Markdown.ColorCode;
|
using Markdown.ColorCode;
|
||||||
|
|
||||||
@@ -13,7 +15,6 @@ namespace highminded.ui.controls;
|
|||||||
|
|
||||||
public partial class ChatUserControl : UserControl
|
public partial class ChatUserControl : UserControl
|
||||||
{
|
{
|
||||||
|
|
||||||
private readonly MarkdownPipeline _pipeline = null!;
|
private readonly MarkdownPipeline _pipeline = null!;
|
||||||
|
|
||||||
public ChatUserControl()
|
public ChatUserControl()
|
||||||
@@ -22,6 +23,29 @@ public partial class ChatUserControl : UserControl
|
|||||||
_pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseColorCode().Build();
|
_pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseColorCode().Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void SendScreenshot()
|
||||||
|
{
|
||||||
|
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||||
|
var fileName = $"screenshot_{timestamp}.png";
|
||||||
|
var filePath = Path.Combine(Environment.CurrentDirectory, fileName);
|
||||||
|
|
||||||
|
var screenshot = await ScreenCapture.CaptureScreenAsync(filePath);
|
||||||
|
if (!screenshot) return;
|
||||||
|
|
||||||
|
using Stream imageStream = File.OpenRead(filePath);
|
||||||
|
BinaryData imageBytes = BinaryData.FromStream(imageStream);
|
||||||
|
|
||||||
|
List<ChatMessage> messages =
|
||||||
|
[
|
||||||
|
new UserChatMessage(
|
||||||
|
ChatMessageContentPart.CreateTextPart("I'm attaching a screenshot of a problem. I want you to read it and give me the appropriate answer."),
|
||||||
|
ChatMessageContentPart.CreateImagePart(imageBytes, "image/png")
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
|
await ProcessChatStreamAsync(messages);
|
||||||
|
}
|
||||||
|
|
||||||
private async void PromptBox_OnKeyDown(object? sender, KeyEventArgs e)
|
private async void PromptBox_OnKeyDown(object? sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -32,8 +56,22 @@ public partial class ChatUserControl : UserControl
|
|||||||
if (prompt is null) return;
|
if (prompt is null) return;
|
||||||
PromptBox.Clear();
|
PromptBox.Clear();
|
||||||
|
|
||||||
AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates =
|
await ProcessChatStreamAsync(prompt);
|
||||||
InMemoryDb.Obj.ChatClient.CompleteChatStreamingAsync(prompt);
|
}
|
||||||
|
catch (Exception err)
|
||||||
|
{
|
||||||
|
ResultBlock.Text = err.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessChatStreamAsync(object promptOrMessages)
|
||||||
|
{
|
||||||
|
AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates = promptOrMessages switch
|
||||||
|
{
|
||||||
|
string prompt => InMemoryDb.Obj.ChatClient.CompleteChatStreamingAsync(prompt),
|
||||||
|
IEnumerable<ChatMessage> messages => InMemoryDb.Obj.ChatClient.CompleteChatStreamingAsync(messages),
|
||||||
|
_ => throw new ArgumentException("Invalid input type", nameof(promptOrMessages))
|
||||||
|
};
|
||||||
|
|
||||||
var responseBuilder = new StringBuilder();
|
var responseBuilder = new StringBuilder();
|
||||||
|
|
||||||
@@ -48,9 +86,4 @@ public partial class ChatUserControl : UserControl
|
|||||||
ResultBlock.Text = html;
|
ResultBlock.Text = html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception err)
|
|
||||||
{
|
|
||||||
ResultBlock.Text = err.Message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -32,6 +32,14 @@
|
|||||||
<TextBlock FontSize="12" Text="SHIFT + \" />
|
<TextBlock FontSize="12" Text="SHIFT + \" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
<Border Background="Black" BorderBrush="#19ffffff" BorderThickness="2" CornerRadius="5"
|
||||||
|
Margin="0 0 10 0">
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
|
Spacing="5" Margin="10 0">
|
||||||
|
<TextBlock FontSize="12" Text="Screen:" />
|
||||||
|
<TextBlock FontSize="12" Text="SHIFT + S" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
<Button Name="ChatBtn" Background="Transparent" Click="ChatBtnClick">
|
<Button Name="ChatBtn" Background="Transparent" Click="ChatBtnClick">
|
||||||
<iconPacks:PackIconLucide Grid.Column="1" Kind="Brain" Height="16" Width="16" />
|
<iconPacks:PackIconLucide Grid.Column="1" Kind="Brain" Height="16" Width="16" />
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -80,12 +80,18 @@ public partial class MainWindow : Window
|
|||||||
bool hasShift = (e.RawEvent.Mask & EventMask.Shift) != EventMask.None;
|
bool hasShift = (e.RawEvent.Mask & EventMask.Shift) != EventMask.None;
|
||||||
bool hasH = e.Data.KeyCode == KeyCode.VcH;
|
bool hasH = e.Data.KeyCode == KeyCode.VcH;
|
||||||
bool hasBackslash = e.Data.KeyCode == KeyCode.VcBackslash;
|
bool hasBackslash = e.Data.KeyCode == KeyCode.VcBackslash;
|
||||||
|
bool hasS = e.Data.KeyCode == KeyCode.VcS;
|
||||||
|
|
||||||
if (hasCtrl && hasShift && hasAlt && hasH)
|
if (hasCtrl && hasShift && hasAlt && hasH)
|
||||||
{
|
{
|
||||||
ShowOverlay();
|
ShowOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasShift && hasS)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() => { _chatUserControl.SendScreenshot(); });
|
||||||
|
}
|
||||||
|
|
||||||
if (hasShift && hasBackslash)
|
if (hasShift && hasBackslash)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
91
utils/ScreenCapture.cs
Normal file
91
utils/ScreenCapture.cs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace highminded.utils;
|
||||||
|
|
||||||
|
public static class ScreenCapture
|
||||||
|
{
|
||||||
|
public static async Task<bool> CaptureScreenAsync(string filePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
return await CaptureWindowsAsync(filePath);
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
return await CaptureMacAsync(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<bool> CaptureWindowsAsync(string filePath)
|
||||||
|
{
|
||||||
|
var script = $$"""
|
||||||
|
Add-Type -AssemblyName System.Windows.Forms
|
||||||
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
|
||||||
|
$bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
|
||||||
|
$bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height
|
||||||
|
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$graphics.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size)
|
||||||
|
$bitmap.Save('{{filePath.Replace("\\", @"\\")}}', [System.Drawing.Imaging.ImageFormat]::Png)
|
||||||
|
Write-Output 'Success'
|
||||||
|
} catch {
|
||||||
|
Write-Output 'Error'
|
||||||
|
} finally {
|
||||||
|
$graphics.Dispose()
|
||||||
|
$bitmap.Dispose()
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "powershell.exe",
|
||||||
|
Arguments = $"-Command \"{script}\"",
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardOutput = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
var output = await process.StandardOutput.ReadToEndAsync();
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
|
||||||
|
return output.Trim() == "Success" && File.Exists(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<bool> CaptureMacAsync(string filePath)
|
||||||
|
{
|
||||||
|
var process = new Process
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "screencapture",
|
||||||
|
Arguments = $"-x \"{filePath}\"",
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
|
||||||
|
return process.ExitCode == 0 && File.Exists(filePath);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user