红豆沙 发表于 2023-7-2 13:16:35

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

一个跨平台的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}"高度固定,宽度绑定到ViewModel的Width属性中,默认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:
      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>
      
      
    </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>
                </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>

                                                      

                                                      

                                                      

                                                      

                                                    </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>只需要设置无边框并且固定大小。悬浮窗的效果就达到了。
我们看看执行效果
<img alt="image-20230702133719931" loading="lazy">
就这样简单的悬浮窗写好了,我们使用一下悬浮窗的搜索功能
<img alt="image-20230702133757221" loading="lazy">
这个就是简单的使用效果,对比其他的工具,这个悬浮窗更简洁,并且跨平台和开源。
<img alt="image-20230702133839454" loading="lazy">
目前的项目结构。
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;
​      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

来源:https://www.cnblogs.com/hejiale010426/archive/2023/07/02/17520719.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 一个跨平台的`ChatGPT`悬浮窗工具