feat: initial commit

This commit is contained in:
tux
2025-06-28 19:21:26 +05:30
commit 85c914d65b
16 changed files with 768 additions and 0 deletions

214
.gitignore vendored Normal file
View File

@@ -0,0 +1,214 @@
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
.vs/
# Build results
*.sln.ide/
[Dd]ebug/
[Rr]elease/
x64/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# NCrunch
_NCrunch_*/
*.ncrunchsolution.user
nCrunchTemp_*
# CodeRush
.cr/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
Events_Avalonia.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#############
## Windows detritus
#############
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
.DS_Store
#################
## Monodevelop
#################
*.userprefs
*.nugetreferenceswitcher
#################
## Rider
#################
.idea
#################
## VS Code
#################
.vscode/
#################
## Cake
#################
tools/*
!tools/packages.config
.nuget
artifacts/
nuget
Avalonia.XBuild.sln
project.lock.json
.idea/*
##################
## BenchmarkDotNet
##################
BenchmarkDotNet.Artifacts/
dirs.sln
##################
# Xcode
##################
Index/
Logs/
ModuleCache.noindex/
Build/Intermediates.noindex/
build-intermediate
obj-Direct2D1/
obj-Skia/
##################
# Vim
##################
.vim
coc-settings.json
.ccls-cache
.ccls
*.map
src/Web/Avalonia.Web.Blazor/wwwroot/*.js
src/Web/Avalonia.Web.Blazor/Interop/Typescript/*.js

11
App.axaml Normal file
View File

@@ -0,0 +1,11 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="highminded.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://IconPacks.Avalonia/Icons.axaml" />
</Application.Styles>
</Application>

23
App.axaml.cs Normal file
View File

@@ -0,0 +1,23 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace highminded;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new highminded.ui.windows.MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}

21
Program.cs Normal file
View File

@@ -0,0 +1,21 @@
using Avalonia;
using System;
namespace highminded;
class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}

1
README.md Normal file
View File

@@ -0,0 +1 @@
# highminded

18
app.manifest Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="highminded.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

37
highminded.csproj Normal file
View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.2"/>
<PackageReference Include="Avalonia.Desktop" Version="11.3.2"/>
<PackageReference Include="Avalonia.HtmlRenderer" Version="11.2.0" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.2"/>
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.2"/>
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.2">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="IconPacks.Avalonia" Version="1.0.0" />
<PackageReference Include="IconPacks.Avalonia.Lucide" Version="1.0.0" />
<PackageReference Include="Markdig" Version="0.41.3" />
<PackageReference Include="Markdown.ColorCode" Version="3.0.0" />
<PackageReference Include="OpenAI" Version="2.2.0-beta.4" />
<PackageReference Include="SharpHook" Version="6.1.2" />
<PackageReference Include="SharpHook.Reactive" Version="6.1.2" />
</ItemGroup>
<ItemGroup>
<Compile Update="ui\windows\MainWindow.axaml.cs">
<DependentUpon>MainWindow.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>

16
highminded.sln Normal file
View File

@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "highminded", "highminded.csproj", "{0A358F60-E66F-41F6-A479-F827B9CD1313}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0A358F60-E66F-41F6-A479-F827B9CD1313}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A358F60-E66F-41F6-A479-F827B9CD1313}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A358F60-E66F-41F6-A479-F827B9CD1313}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A358F60-E66F-41F6-A479-F827B9CD1313}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="highminded.ui.controls.ChatUserControl">
<Grid RowDefinitions="*,Auto" Margin="15" RowSpacing="10">
<HtmlPanel Grid.Row="0" Name="ResultBlock" BaseStylesheet="* { font: Inter; color: white; }" />
<TextBox Grid.Row="1" Name="PromptBox" CornerRadius="5" TextWrapping="Wrap"
KeyDown="PromptBox_OnKeyDown" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,67 @@
using Avalonia.Controls;
using System;
using System.ClientModel;
using System.Text;
using Avalonia.Input;
using highminded.utils;
using OpenAI.Chat;
using OpenAI;
using Markdig;
using Markdown.ColorCode;
namespace highminded.ui.controls;
public partial class ChatUserControl : UserControl
{
// OpenAI
private readonly OpenAI.Chat.ChatClient _client = null!;
private readonly MarkdownPipeline _pipeline = null!;
public ChatUserControl()
{
InitializeComponent();
_client = new ChatClient(
model: InMemoryDb.Obj.settingsManager.Settings.Model,
credential: new ApiKeyCredential(InMemoryDb.Obj.settingsManager.Settings.ApiKey),
options: new OpenAIClientOptions
{
Endpoint = new Uri(InMemoryDb.Obj.settingsManager.Settings.ApiURL)
});
_pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseColorCode().Build();
}
private async void PromptBox_OnKeyDown(object? sender, KeyEventArgs e)
{
try
{
if (e.Key != Key.Enter) return;
var prompt = PromptBox.Text;
if (prompt is null) return;
PromptBox.Clear();
AsyncCollectionResult<StreamingChatCompletionUpdate> completionUpdates =
_client.CompleteChatStreamingAsync(prompt);
var responseBuilder = new StringBuilder();
await foreach (var completionUpdate in completionUpdates)
{
if (completionUpdate.ContentUpdate.Count <= 0) continue;
var token = completionUpdate.ContentUpdate[0].Text;
responseBuilder.Append(token);
var html = Markdig.Markdown.ToHtml(responseBuilder.ToString(), _pipeline);
ResultBlock.Text = html;
}
}
catch (Exception err)
{
ResultBlock.Text = err.Message;
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="highminded.ui.controls.SettingsUserControl">
<Grid Margin="15" RowSpacing="10" ColumnSpacing="10" RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="*,*">
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Text="Model" />
<TextBox Name="ModelTextBox" />
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1">
<TextBlock Text="API URL" />
<TextBox Name="ApiUrlTextBox" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<TextBlock Text="API Key" />
<TextBox Name="ApiKeyTextBox" PasswordChar="*" />
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<Button Name="SaveSettingsBtn" Content="Save" Click="SaveSettingsBtn_OnClick"/>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,30 @@
using System;
using System.Diagnostics;
using System.Reflection;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using highminded.utils;
namespace highminded.ui.controls;
public partial class SettingsUserControl : UserControl
{
public SettingsUserControl()
{
InitializeComponent();
ModelTextBox.Text = InMemoryDb.Obj.settingsManager.Settings.Model;
ApiUrlTextBox.Text = InMemoryDb.Obj.settingsManager.Settings.ApiURL;
ApiKeyTextBox.Text = InMemoryDb.Obj.settingsManager.Settings.ApiKey;
}
private void SaveSettingsBtn_OnClick(object? sender, RoutedEventArgs e)
{
InMemoryDb.Obj.settingsManager.Settings.Model = ModelTextBox.Text;
InMemoryDb.Obj.settingsManager.Settings.ApiURL= ApiUrlTextBox.Text;
InMemoryDb.Obj.settingsManager.Settings.ApiKey = ApiKeyTextBox.Text;
InMemoryDb.Obj.settingsManager.Save();
}
}

View File

@@ -0,0 +1,53 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:iconPacks="https://github.com/MahApps/IconPacks.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="highminded.ui.windows.MainWindow"
Title="highminded"
Height="600"
Width="800"
ShowInTaskbar="False"
ShowActivated="True"
Topmost="True"
WindowStartupLocation="CenterScreen"
SystemDecorations="None"
TransparencyLevelHint="Transparent"
Background="Transparent"
Foreground="#fff"
TransparencyBackgroundFallback="Transparent"
FontSize="16">
<Grid RowDefinitions="Auto,*" Margin="5" RowSpacing="10">
<Border Grid.Row="0" CornerRadius="5" Background="Black" Opacity="0.8" PointerPressed="OnPointerPressed">
<Grid ColumnDefinitions="*,Auto" Margin="15 10">
<TextBlock Grid.Column="0" VerticalAlignment="Center" Margin="5">High Minded</TextBlock>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center"
HorizontalAlignment="Center">
<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="Hide:" />
<TextBlock FontSize="12" Text="CTRL + \" />
</StackPanel>
</Border>
<Button Name="ChatBtn" Background="Transparent" Click="ChatBtnClick">
<iconPacks:PackIconLucide Grid.Column="1" Kind="Brain" Height="16" Width="16" />
</Button>
<Button Name="SettingsBtn" Background="Transparent" Click="SettingBtnClick">
<iconPacks:PackIconLucide Grid.Column="1" Kind="Settings" Height="16" Width="16" />
</Button>
<Button Name="HideBtn" Background="Transparent">
<iconPacks:PackIconLucide Grid.Column="1" Kind="X" Height="13" Width="13" />
</Button>
</StackPanel>
</Grid>
</Border>
<Border Grid.Row="1" CornerRadius="5" Background="Black" Opacity="0.8">
<UserControl Name="UControl" />
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,167 @@
using Avalonia.Controls;
using System;
using System.Runtime.InteropServices;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Threading;
using highminded.ui.controls;
using SharpHook;
using SharpHook.Data;
namespace highminded.ui.windows;
public partial class MainWindow : Window
{
// MacOS
private const string ObjCRuntime = "/usr/lib/libobjc.dylib";
[DllImport(ObjCRuntime, EntryPoint = "objc_msgSend")]
private static extern void ObjcMsgSendInt(nint receiver, nint selector, int value);
[DllImport(ObjCRuntime, EntryPoint = "sel_registerName")]
private static extern nint RegisterName(string name);
// Windows
[DllImport("user32.dll")]
private static extern bool SetWindowDisplayAffinity(IntPtr hWnd, uint dwAffinity);
// Controls
private readonly ChatUserControl _chatUserControl = new ChatUserControl();
private readonly SettingsUserControl _settingsUserControl = new SettingsUserControl();
// Hotkey
private readonly TaskPoolGlobalHook _hook = new TaskPoolGlobalHook();
public MainWindow()
{
InitializeComponent();
UControl.Content = _chatUserControl;
ChatBtnActive();
HideOverlay();
// Global Hotkey
_hook.KeyPressed += OnKeyPressed;
_hook.RunAsync();
}
private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
BeginMoveDrag(e);
}
private void ChatBtnClick(object? sender, RoutedEventArgs e)
{
UControl.Content = _chatUserControl;
ChatBtnActive();
}
private void SettingBtnClick(object? sender, RoutedEventArgs e)
{
UControl.Content = _settingsUserControl;
SettingsBtnActive();
}
private void ChatBtnActive()
{
ChatBtn.Background = new SolidColorBrush(Color.FromArgb(25, 255, 255, 255));
SettingsBtn.Background = new SolidColorBrush(Colors.Transparent);
}
private void SettingsBtnActive()
{
ChatBtn.Background = new SolidColorBrush(Colors.Transparent);
SettingsBtn.Background = new SolidColorBrush(Color.FromArgb(25, 255, 255, 255));
}
private void OnKeyPressed(object? sender, KeyboardHookEventArgs e)
{
bool hasCtrl = (e.RawEvent.Mask & EventMask.Ctrl) != EventMask.None;
bool hasAlt = (e.RawEvent.Mask & EventMask.Alt) != EventMask.None;
bool hasShift = (e.RawEvent.Mask & EventMask.Shift) != EventMask.None;
bool hasH = e.Data.KeyCode == KeyCode.VcH;
bool hasBackslash = e.Data.KeyCode == KeyCode.VcBackslash;
if (hasCtrl && hasShift && hasAlt && hasH)
{
ShowOverlay();
}
if (hasShift && hasBackslash)
{
Dispatcher.UIThread.Post(() =>
{
if (WindowState == WindowState.Minimized || !IsVisible)
{
Show();
WindowState = WindowState.Normal;
Activate();
Topmost = true;
}
else
{
Hide();
}
});
}
}
private void ShowOverlay()
{
var handle = TryGetPlatformHandle();
if (handle is null || handle.Handle == IntPtr.Zero)
{
Console.WriteLine("Invalid window handle.");
return;
}
var hwnd = handle.Handle;
switch (handle.HandleDescriptor)
{
case "HWND":
{
const uint include = 0x00000000;
SetWindowDisplayAffinity(hwnd, include);
break;
}
case "NSWindow":
{
const int include = 2;
var sharingTypeSelector = RegisterName("setSharingType:");
ObjcMsgSendInt(hwnd, sharingTypeSelector, include);
break;
}
}
}
private void HideOverlay()
{
var handle = TryGetPlatformHandle();
if (handle is null || handle.Handle == IntPtr.Zero)
{
Console.WriteLine("Invalid window handle.");
return;
}
var hwnd = handle.Handle;
switch (handle.HandleDescriptor)
{
case "HWND":
{
const uint exclude = 0x00000011;
SetWindowDisplayAffinity(hwnd, exclude);
break;
}
case "NSWindow":
{
const int exclude = 0;
var sharingTypeSelector = RegisterName("setSharingType:");
ObjcMsgSendInt(hwnd, sharingTypeSelector, exclude);
break;
}
}
}
}

15
utils/InMemoryDB.cs Normal file
View File

@@ -0,0 +1,15 @@
using System;
using System.IO;
using Path = Avalonia.Controls.Shapes.Path;
namespace highminded.utils;
public class InMemoryDb
{
// Initialize Singleton Class
InMemoryDb() { }
public static readonly InMemoryDb Obj = new InMemoryDb();
public SettingsManager<AppSettings> settingsManager = new SettingsManager<AppSettings>();
}

51
utils/SetttingsManager.cs Normal file
View File

@@ -0,0 +1,51 @@
using System;
using System.IO;
using System.Text.Json;
namespace highminded.utils;
public class AppSettings
{
public string Model { get; set; }
public string ApiURL { get; set; }
public string ApiKey { get; set; }
}
public class SettingsManager<T> where T : class, new()
{
private readonly string _settingsPath;
public T Settings { get; private set; }
public SettingsManager(string appName = "highminded")
{
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var appFolder = Path.Combine(appData, appName);
Directory.CreateDirectory(appFolder);
_settingsPath = Path.Combine(appFolder, "settings.json");
Settings = Load();
}
private T Load()
{
if (!File.Exists(_settingsPath))
return new T();
try
{
string json = File.ReadAllText(_settingsPath);
return JsonSerializer.Deserialize<T>(json) ?? new T();
}
catch
{
return new T(); // Fallback to default settings if error occurs
}
}
public void Save()
{
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(Settings, options);
File.WriteAllText(_settingsPath, json);
}
}