首页 > 其他分享 >一个跨平台的`ChatGPT`悬浮窗工具

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

时间:2023-07-02 13:55:45浏览次数:54  
标签:插件 Gotrays PlugIn 悬浮 跨平台 override Suspension 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}"

高度固定,宽度绑定到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>

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

我们看看执行效果

image-20230702133719931

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

image-20230702133757221

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

image-20230702133839454

目前的项目结构。

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

标签:插件,Gotrays,PlugIn,悬浮,跨平台,override,Suspension,ChatGPT
From: https://www.cnblogs.com/hejiale010426/p/17520719.html

相关文章

  • 零代码编程:用ChatGPT自动输入账号密码来登陆网站
    现在很多网站都需要登陆账号和密码才能访问。如果要进行一些批量自动化操作,首先要做的就是模拟自动登陆。以萝卜投研网站为例:https://robo.datayes.com/v2/fastreport/industry打开网站后,默认出现这样一个登陆界面,要点击右上角,才会出现账号密码登陆按钮:<divclass="login-switch-bt......
  • 靳宇灵 | CHATGPT真的很强大,很快帮我解决了tp5对接腾讯cos存储的SDK,NB!!
    php请求腾讯云cos存储SDK报错ThrowableErrorinClient.phpline229致命错误:CalltoundefinedfunctionCos\region_map()这个错误的原因是您在使用腾讯云cos存储SDK时,调用了一个未定义的函数 Cos\region_map()。首先,您需要确保安装了腾讯云cos存储SDK。可以通过Composer进......
  • 将ChatGPT变成Midjourney提示生成器
    已经有人总结过可以让ChatGPT作为Midjourney图像生成的模板。在本文中,我们将展示如何根据个人用例创建这些提示,这可以让ChatGPT生成的提示可控性更高。 https://avoid.overfit.cn/post/60d45f154b7943258f86f8bc7150e79b......
  • ChatGPT 讲的笑话90%是重复的?归纳出来只有这25个?
    作者|小戏、Python幽默,似乎一直是一种专属于人的语言技巧,通过一些简单的谐音、双关,让错位的事张冠李戴,让一些可能普普通通的事变得荒诞,神奇的就可以在人际之间的心照不宣中获得幽默,让人捧腹,获得一种奇异的感情力量。如果让机器拥有“痛觉”,是发展出具有具身人工智能关键一步,那么......
  • 只要四步就可以拍悬浮的相片 太简单了
         第一步:找块空地。 第二步:在空地上,弄一小块水渍。 第三步:站在水渍旁边。 第四步:拍照作者:古道轻风......
  • 行行AI人才直播第5期:系列课-AI理解及ChatGPT从基础到高级应用
    当前,人工智能是全世界研究的重点对象,也是人们茶余饭后讨论的经典话题。自从OpenAI发布ChatGPT-4之后,似乎无论是在工作、娱乐、甚至是日常生活中,我们都能感受到AI带来的便利和改变。然而,对于很多人来说,人工智能还是一个比较抽象的概念,既讲不清楚什么是人工智能,对ChatGPT的了......
  • 零代码编程:用ChatGPT来批量合并多个PDF文件
    一个文件夹里面有38个PDF文件,现在想合并成一个PDF文件。用ChatGPT可以非常简单的实现。在ChatGPT中输入提示词如下:这两个文件夹里面有多个PDF文件,写一段Python程序,将文件夹里面的PDF文件按照文件标题名合并成一个PDF文件,然后保存到文件夹中F:\BaiduNetdiskDownload\小兔兵兵第1季绘......
  • 文心一言 VS 讯飞星火 VS chatgpt (51)-- 算法导论6.2 3题
    三、当元素A[i]比其孩子的值都大时,调用MAX-HEAPIFY(A.i)会有什么结果?文心一言:当元素A[i]比其孩子的的值都大时,调用MAX-HEAPIFY(A,i)会将A[i]与其孩子中的最小值进行交换,并将A[i]视为新的根节点。这个操作会使得以A[i]为根节点的子树满足最大堆的性质,即根节点比其左......
  • 文心一言 VS 讯飞星火 VS chatgpt (50)-- 算法导论6.2 2题
    二、参考过程MAX-HEAPIFY,写出能够维护相应最小堆的MIN-HEAPIFY(A,i)的伪代码,并比较MIN-HEAPIFY与MAX-HEAPIFY的运行时间。文心一言:MIN-HEAPIFY(A,i)的伪代码如下:1.将节点i及其子树中的最小元素交换到根节点i;2.对于左子树,递归调用MIN-HEAPIFY(A,2i);3.对于右子树,递归调......
  • 文心一言 VS 讯飞星火 VS chatgpt (50)-- 算法导论6.2 2题
    二、参考过程MAX-HEAPIFY,写出能够维护相应最小堆的MIN-HEAPIFY(A,i)的伪代码,并比较MIN-HEAPIFY与MAX-HEAPIFY的运行时间。文心一言:MIN-HEAPIFY(A,i)的伪代码如下:1.将节点i及其子树中的最小元素交换到根节点i;2.对于左子树,递归调用MIN-HEAPIFY(A,2i);3.对于右子树,递归调用MIN-H......