一个跨平台的`ChatGPT`悬浮窗工具

  • 一个跨平台的`ChatGPT`悬浮窗工具已关闭评论
  • 118 次浏览
  • A+
所属分类:.NET技术
摘要

使用avalonia实现的ChatGPT的工具,设计成悬浮窗,并且支持插件。在使用avalonia实现悬浮窗也是非常的简单的。


一个跨平台的ChatGPT悬浮窗工具

使用avalonia实现的ChatGPT的工具,设计成悬浮窗,并且支持插件。

如何实现悬浮窗?

在使用avalonia实现悬浮窗也是非常的简单的。

实现我们需要将窗体设置成无边框

Window根节点添加一下属性,想要在Linux下生效请务必添加SystemDecorations属性

ExtendClientAreaToDecorationsHint="True" ExtendClientAreaChromeHints="NoChrome" ExtendClientAreaTitleBarHeightHint="-1" SystemDecorations="None" 

这样我们的窗口就设置成了无边框。

然后我们还需要将窗体的大小固定,

Height="50" MaxHeight="50" Width="{Binding Width}" MaxWidth="{Binding Width}" 

高度固定,宽度绑定到ViewModelWidth属性中,默认270

接下来给出所有代码,

<Window xmlns="https://github.com/avaloniaui"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"         xmlns:vm="using:Gotrays.Suspension.Client.ViewModels"         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"         xmlns:valueConverter="clr-namespace:Gotrays.Suspension.Client.ValueConverter"         xmlns:md="clr-namespace:Markdown.Avalonia;assembly=Markdown.Avalonia"         xmlns:avedit="https://github.com/avaloniaui/avaloniaedit"         xmlns:ctxt="clr-namespace:ColorTextBlock.Avalonia;assembly=ColorTextBlock.Avalonia"         mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"         x:Class="Gotrays.Suspension.Client.Views.MainWindow"         x:DataType="vm:MainWindowViewModel"         ExtendClientAreaToDecorationsHint="True"         ExtendClientAreaChromeHints="NoChrome"         ExtendClientAreaTitleBarHeightHint="-1"         SystemDecorations="None"         WindowStartupLocation="CenterScreen"         Height="50"         MaxHeight="50"         Width="{Binding Width}"         MaxWidth="{Binding Width}"         Icon="/Assets/ai.png"         Title="Gotrays.Suspension.Client">      <Window.Resources>         <valueConverter:ImageConverter x:Key="ImageConverter" />         <valueConverter:ChatToStyleConverter x:Key="ChatToStyleConverter" />     </Window.Resources>      <Design.DataContext>         <vm:MainWindowViewModel />     </Design.DataContext>      <Window.Styles>         <Style Selector="Window">             <Setter Property="BorderThickness" Value="0" />             <Setter Property="Padding" Value="0" />             <Setter Property="Background" Value="Transparent" />             <Setter Property="BorderBrush" Value="Transparent" />         </Style>         <Style Selector="TextBox.red:pointerover">             <Setter Property="Opacity" Value="1" />         </Style>     </Window.Styles>      <Border Name="MainBorder" CornerRadius="1000" Background="Black" Margin="0,0,0,0" Padding="0,0,0,0"             HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Height="50">         <Grid>             <!-- 图标 -->             <Image Source="../Assets/ai.png" Name="Logo" HorizontalAlignment="Left" VerticalAlignment="Center"                    Width="46"                    Tapped="Logo_OnTapped"                    RenderOptions.BitmapInterpolationMode="HighQuality"                    PointerPressed="OnLogoClick"                    PointerEntered="Logo_OnPointerEntered"                    PointerExited="Logo_OnPointerExited"                    Height="46" Margin="0,0,0,0" />              <!-- 模型选择 -->             <Popup Name="ModulePopup" IsOpen="False" PlacementTarget="{Binding ElementName=MainBorder}"                    PlacementMode="Top">                 <StackPanel Margin="5">                     <Border Background="#1F1F1F" BorderBrush="Black" BorderThickness="1" CornerRadius="12"                             MaxHeight="400" Width="120">                         <ScrollViewer Name="ModuleScrollViewer" VerticalScrollBarVisibility="Auto">                             <ItemsControl CornerRadius="12" ItemsSource="{Binding Modules}" Margin="2">                                 <ItemsControl.ItemTemplate>                                     <DataTemplate>                                         <Border Margin="5"                                                 Background="{Binding Color}"                                                 PointerExited="OnSelectStackPointerExited"                                                 PointerEntered="OnSelectStackPointerEntered"                                                 PointerPressed="OnSelectStackPointerPressed"                                                 Tag="{Binding GetThis}"                                                 CornerRadius="8">                                             <!-- 左边显示图标,右边显示名称 -->                                             <StackPanel Orientation="Horizontal">                                                 <Image                                                     RenderOptions.BitmapInterpolationMode="HighQuality"                                                     Source="{Binding Icon, Converter={StaticResource ImageConverter}}"                                                     HorizontalAlignment="Left"                                                     Width="20"                                                     Height="20" />                                                 <TextBlock TextWrapping="Wrap" Width="60" Text="{Binding Title}"                                                            Margin="5" Foreground="White" />                                             </StackPanel>                                         </Border>                                     </DataTemplate>                                 </ItemsControl.ItemTemplate>                             </ItemsControl>                         </ScrollViewer>                     </Border>                 </StackPanel>             </Popup>              <!-- 静止状态下的搜索按钮 -->             <Border PointerPressed="SearchBorder_OnPointerPressed"                     PointerEntered="searchBorder_PointerEnter"                     PointerExited="OnPointerExited"                     Name="searchBorder"                     CornerRadius="1000" Background="#000000" BorderBrush="#FFFFFF"                     BorderThickness="1" Margin="50,0,0,0" Padding="0,0,0,0" HorizontalAlignment="Left"                     VerticalAlignment="Center" Width="46" Height="46" Cursor="Hand">                 <Image Source="../Assets/search.png"                        RenderOptions.BitmapInterpolationMode="HighQuality"                        HorizontalAlignment="Center" VerticalAlignment="Center"                        Width="20" Height="20" Margin="0,0,0,0" />             </Border>              <!-- 当点击搜索按钮时,显示搜索框 -->             <TextBox FontSize="20" Name="SearchText" Margin="50,0,0,0" IsVisible="False" Width="0" Height="40"                      HorizontalAlignment="Left" VerticalAlignment="Center">                 <TextBox.Styles>                     <Styles>                         <Style Selector="TextBox">                             <Setter Property="CornerRadius" Value="0,50,50,0"></Setter>                         </Style>                     </Styles>                 </TextBox.Styles>                 <TextBox.Transitions>                     <Transitions>                         <DoubleTransition Property="Width" Duration="0:0:0.1" />                     </Transitions>                 </TextBox.Transitions>             </TextBox>             <!-- 消息显示区域 -->             <Popup x:Name="MessagePopup"                    IsOpen="False"                    PlacementTarget="{Binding ElementName=MainBorder}"                    PlacementMode="Bottom">                 <StackPanel                     PointerEntered="MessagePopup_OnPointerEntered"                     PointerExited="MessagePopup_OnPointerExited" Margin="5">                     <Border Name="MessageBorder"                             Background="#1F1F1F"                             BorderBrush="Black"                             BorderThickness="1"                             CornerRadius="12"                             MaxHeight="300">                         <ScrollViewer Name="ScrollViewer" VerticalScrollBarVisibility="Auto">                             <ItemsControl ItemsSource="{Binding Messages}" CornerRadius="12" Margin="2">                                 <ItemsControl.ItemTemplate>                                     <DataTemplate>                                         <StackPanel Margin="5">                                             <StackPanel.Resources>                                                 <valueConverter:ChatToBackgroundConverter                                                     x:Key="ChatToBackgroundConverter" />                                             </StackPanel.Resources>                                             <Border                                                 Background="{Binding Chat, Converter={StaticResource ChatToBackgroundConverter}}"                                                 CornerRadius="5">                                                  <md:MarkdownScrollViewer                                                     VerticalAlignment="Stretch"                                                     MarkdownStyleName="Standard"                                                     SaveScrollValueWhenContentUpdated="True"                                                     Markdown="{Binding Message}">                                                     <md:MarkdownScrollViewer.Styles>                                                          <Style Selector="ctxt|CCode">                                                             <Style.Setters>                                                                 <Setter Property="BorderBrush" Value="Green" />                                                                 <Setter Property="BorderThickness" Value="2" />                                                                 <Setter Property="Padding" Value="2" />                                                                 <Setter Property="MonospaceFontFamily" Value="Meiryo" />                                                                 <Setter Property="Foreground" Value="DarkGreen" />                                                                 <Setter Property="Background" Value="LightGreen" />                                                             </Style.Setters>                                                         </Style>                                                          <Style Selector="Border.CodeBlock">                                                             <Style.Setters>                                                                 <Setter Property="BorderBrush" Value="Blue" />                                                                 <Setter Property="BorderThickness" Value="0,5,0,5" />                                                                 <Setter Property="Margin" Value="5,0,5,0" />                                                                 <Setter Property="Background" Value="LightBlue" />                                                             </Style.Setters>                                                         </Style>                                                          <Style Selector="TextBlock.CodeBlock">                                                             <Style.Setters>                                                                 <Setter Property="Foreground" Value="DarkBlue" />                                                                 <Setter Property="FontFamily" Value="Meiryo" />                                                             </Style.Setters>                                                         </Style>                                                          <Style Selector="avedit|TextEditor">                                                             <Setter Property="Background" Value="Gray" />                                                             <Setter Property="CornerRadius" Value="10"></Setter>                                                         </Style>                                                      </md:MarkdownScrollViewer.Styles>                                                 </md:MarkdownScrollViewer>                                             </Border>                                         </StackPanel>                                     </DataTemplate>                                 </ItemsControl.ItemTemplate>                             </ItemsControl>                         </ScrollViewer>                     </Border>                 </StackPanel>             </Popup>          </Grid>         <Border.Transitions>             <Transitions>                 <DoubleTransition Property="Width" Duration="0:0:0.2" />             </Transitions>         </Border.Transitions>     </Border>  </Window> 

只需要设置无边框并且固定大小。悬浮窗的效果就达到了。

我们看看执行效果

一个跨平台的`ChatGPT`悬浮窗工具

就这样简单的悬浮窗写好了,我们使用一下悬浮窗的搜索功能

一个跨平台的`ChatGPT`悬浮窗工具

这个就是简单的使用效果,对比其他的工具,这个悬浮窗更简洁,并且跨平台和开源。

一个跨平台的`ChatGPT`悬浮窗工具

目前的项目结构。

plugin下面的项目是默认的插件,用于搜索系统文件(未完善)

Gotrays.Suspension.Client则是实际的客户端。

Gotrays.Suspension.PlugIn则是插件定义的接口规范。

Gotrays.Update则是检查更新程序,用于更新主程序。

实现插件

plug-in

插件模块,用于扩展功能。

插件开发

1. 创建插件项目

在解决方案中创建一个类库项目,项目名称以Gotrays.Suspension.PlugIn.开头,例如Gotrays.Suspension.PlugIn.Test
然后在项目中依赖Gotrays.Suspension.PlugIn类库。

2. 创建插件类

在项目中创建一个类,继承Gotrays.Suspension.PlugIn.PlugInBase类,例如:

using Gotrays.Suspension.PlugIn;  public class SystemTools : PlugInBase {     public SystemTools()     {         Name = "系统搜索";  ​        // 获取system.png嵌入资源的Stream ​        var stream = GetType().Assembly.GetManifestResourceStream("SystemTools.system.png"); ​        if (stream == null) return;  ​        // 读取Stream到byte数组 ​        var bytes = new byte[stream.Length]; ​        var read = stream.Read(bytes, 0, bytes.Length); ​        Icon = bytes; ​    }  ​    // 搜索触发 ​    public override async Task SearchAsync(string value) ​    { ​        // 打开系统搜索 ​        Process.Start("explorer.exe", "search://" + value);  ​        await Task.CompletedTask; ​    } ​     ​    protected override async Task InitAsync(IServiceCollection services){ ​        // 插件首次加载时执行 ​    } ​    public override async Task BuilderServiceAsync(IServiceProvider provider) ​    { ​        // 这里可以得到服务提供者,可以通过服务提供者获取其他服务 ​    } ​    protected override void Selection() ​    { ​        //  当插件被选中时执行 ​    } ​     ​    protected override void UnSelection() ​    { ​        // 当插件被取消选中时执行 ​    } ​     ​    protected override async Task UnloadAsync() ​    { ​        // 当插件被卸载插件发生 ​    } ​     } 

工具服务会进行自动发现,无需手动注册。
只需要将程序集放置在./plug-in目录下即可。
服务会在一个程序集中发现所有的插件类,并且进行注册。

按照上面的方式非常的简单就集成了插件。

开源地址

Gitee:https://gitee.com/gotrays/gotrays-suspension

Github:https://github.com/239573049/Suspension
技术交流群:737776595