一個跨平臺的`ChatGPT`懸浮窗工具

来源:https://www.cnblogs.com/hejiale010426/archive/2023/07/02/17520719.html
-Advertisement-
Play Games

# 一個跨平臺的`ChatGPT`懸浮窗工具 使用`avalonia`實現的`ChatGPT`的工具,設計成懸浮窗,並且支持插件。 ## 如何實現懸浮窗? 在使用`avalonia`實現懸浮窗也是非常的簡單的。 實現我們需要將窗體設置成無邊框 在`Window`根節點添加一下屬性,想要在Linux下 ...


一個跨平臺的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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • **原文鏈接:** [Go 語言 context 都能做什麼?](https://mp.weixin.qq.com/s/7IliODEUt3JpEuzL8K_sOg) 很多 Go 項目的源碼,在讀的過程中會發現一個很常見的參數 `ctx`,而且基本都是作為函數的第一個參數。 為什麼要這麼寫呢?這個參 ...
  • POM( Project Object Model,項目對象模型 ) 是 Maven 工程的基本工作單元,它是一個 XML 文件,包含了項目的基本信息,用於描述項目如何構建,聲明項目依賴等等。執行任務或目標時,Maven 會在當前目錄中查找並讀取 POM,獲取所需的配置信息,然後執行目標。 1、基本 ...
  • # HttpServletResponse對象 ## 基本介紹 ​ Web伺服器收到客戶端的http請求,會針對每次請求,分別創建一個用於**代表請求**的 request對象 和**代表響應**的 response對象。 ​ request 和 response對象 代表請求和響應:**獲取客戶瑞 ...
  • 不知不覺,《C++面試八股文》已經更新30篇了,這是我第一次寫技術博客,由於個人能力有限,出現了不少紕漏,在此向各位讀者小伙伴們致歉。 為了不誤導更多的小伙伴,以後會不定期的出勘誤文章,請各位小伙伴留意。 在《[C++面試八股文:C++中,設計一個類要註意哪些東西?](https://zhuanla ...
  • ### 1、背景介紹 前兩天,現場的同事使用開發的程式測試時,發現日誌中報`etcdserver: mvcc: database space exceeded`,導致 etcd 無法連接。很奇怪,我們開發的程式只用到了 etcd 做程式的主備,並沒有往 etcd 中寫入大量的數據,為什麼會造成 et ...
  • # WPF複習知識點記錄 由於近幾年主要在做Web項目,客戶端的項目主要是以維護為主,感覺對於基礎知識的掌握沒有那麼牢靠,趁著這個周末重新複習下WPF的相關知識。 文章內容主要來自大佬劉鐵錳老師的經典著作《深入淺出WPF》。 因為是複習,所以知識內容不會一一記錄,如有需要瞭解更多可以看書中內容。 * ...
  • 本章將和大家分享使用 SignalR 生成實時應用的基礎知識。通過本文您將學習如何:使用ASP.NET Core SignalR + MVC + Vue 2.x + require 最終創建一個正常運行的簡易聊天應用。 ...
  • 假設有以下兩個實體: public class Student { public int StuID { get; set; } public string? Name { get; set; } public IEnumerable<Homework>? Homeworks { get; set; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...