# 一個跨平臺的`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}"
高度固定,寬度綁定到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: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>
只需要設置無邊框並且固定大小。懸浮窗的效果就達到了。
我們看看執行效果
就這樣簡單的懸浮窗寫好了,我們使用一下懸浮窗的搜索功能
這個就是簡單的使用效果,對比其他的工具,這個懸浮窗更簡潔,並且跨平臺和開源。
目前的項目結構。
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