Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88149d1458 | ||
|
|
79fd79c01d | ||
|
|
7550102406 | ||
|
|
98996609a3 | ||
|
|
65464d22e3 | ||
|
|
0ca7f7869d | ||
|
|
af47e7c763 | ||
|
|
04058f12c1 | ||
|
|
3a7d39971a | ||
|
|
b0b5476c48 | ||
|
|
b3158a81b8 | ||
|
|
83400bceed | ||
|
|
1133f2d785 | ||
|
|
14dde760b0 | ||
|
|
e0a2685dcf | ||
|
|
ca2f8c4852 | ||
|
|
8f81aa526e | ||
|
|
360a0f28c7 | ||
|
|
4d6e04acb8 | ||
|
|
dc538ee5ce | ||
|
|
16d18878e0 | ||
|
|
893f226463 |
@@ -1,20 +1,20 @@
|
|||||||
<Application xmlns="https://github.com/avaloniaui"
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
x:Class="Needlework.Net.Desktop.App"
|
x:Class="Needlework.Net.Desktop.App"
|
||||||
RequestedThemeVariant="Dark"
|
|
||||||
xmlns:local="using:Needlework.Net.Desktop"
|
xmlns:local="using:Needlework.Net.Desktop"
|
||||||
xmlns:converters="using:Needlework.Net.Desktop.Converters"
|
xmlns:converters="using:Needlework.Net.Desktop.Converters"
|
||||||
xmlns:sukiUi="clr-namespace:SukiUI;assembly=SukiUI"
|
xmlns:sty="using:FluentAvalonia.Styling"
|
||||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia">
|
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
|
RequestedThemeVariant="Dark">
|
||||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||||
<Application.DataTemplates>
|
<Application.DataTemplates>
|
||||||
<local:ViewLocator/>
|
<local:ViewLocator/>
|
||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme></FluentTheme>
|
<sty:FluentAvaloniaTheme PreferSystemTheme="False" PreferUserAccentColor="False" />
|
||||||
<sukiUi:SukiTheme ThemeColor="Blue" />
|
|
||||||
<materialIcons:MaterialIconStyles />
|
<materialIcons:MaterialIconStyles />
|
||||||
|
<StyleInclude Source="Controls/Card.axaml"/>
|
||||||
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
|
<StyleInclude Source="avares://AvaloniaEdit/Themes/Fluent/AvaloniaEdit.xaml" />
|
||||||
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
|
|||||||
BIN
Needlework.Net.Desktop/Assets/Icons/home.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Needlework.Net.Desktop/Assets/Icons/info-circle.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Needlework.Net.Desktop/Assets/Icons/list-alt.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Needlework.Net.Desktop/Assets/Icons/plug.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Needlework.Net.Desktop/Assets/Icons/terminal.png
Normal file
|
After Width: | Height: | Size: 938 B |
66
Needlework.Net.Desktop/Controls/BusyArea.axaml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:controls="using:Needlework.Net.Desktop.Controls"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Needlework.Net.Desktop.Controls.BusyArea">
|
||||||
|
<UserControl.Styles>
|
||||||
|
<Style Selector="controls|BusyArea">
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Panel>
|
||||||
|
<ContentControl Content="{TemplateBinding Content}"/>
|
||||||
|
<DockPanel Name="LoadingBusyArea"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
LastChildFill="True">
|
||||||
|
<TextBlock Margin="16"
|
||||||
|
DockPanel.Dock="Bottom"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
FontWeight="DemiBold"
|
||||||
|
Text="{TemplateBinding BusyText}"/>
|
||||||
|
<ProgressBar
|
||||||
|
Width="100"
|
||||||
|
IsIndeterminate="True"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</DockPanel>
|
||||||
|
</Panel>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|BusyArea DockPanel#LoadingBusyArea">
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Transitions>
|
||||||
|
<DoubleTransition Property="Opacity" Duration="0:0:0.3" />
|
||||||
|
</Transitions>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|BusyArea[IsBusy=True] DockPanel#LoadingBusyArea">
|
||||||
|
<Setter Property="Opacity" Value="1"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|BusyArea[IsBusy=False] DockPanel#LoadingBusyArea">
|
||||||
|
<Setter Property="Opacity" Value="0"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|BusyArea ContentControl">
|
||||||
|
<Setter Property="Transitions">
|
||||||
|
<Transitions>
|
||||||
|
<DoubleTransition Property="Opacity" Duration="0:0:0.3"/>
|
||||||
|
</Transitions>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|BusyArea[IsBusy=True] ContentControl">
|
||||||
|
<Setter Property="Opacity" Value="0.1"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="controls|BusyArea[IsBusy=False] ContentControl">
|
||||||
|
<Setter Property="Opacity" Value="1"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
</UserControl>
|
||||||
30
Needlework.Net.Desktop/Controls/BusyArea.axaml.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Desktop.Controls;
|
||||||
|
|
||||||
|
public partial class BusyArea : UserControl
|
||||||
|
{
|
||||||
|
public BusyArea()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> IsBusyProperty =
|
||||||
|
AvaloniaProperty.Register<BusyArea, bool>(nameof(IsBusy), defaultValue: false);
|
||||||
|
|
||||||
|
public bool IsBusy
|
||||||
|
{
|
||||||
|
get { return GetValue(IsBusyProperty); }
|
||||||
|
set { SetValue(IsBusyProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<string?> BusyTextProperty =
|
||||||
|
AvaloniaProperty.Register<BusyArea, string?>(nameof(BusyText), defaultValue: null);
|
||||||
|
|
||||||
|
public string? BusyText
|
||||||
|
{
|
||||||
|
get => GetValue(BusyTextProperty);
|
||||||
|
set => SetValue(BusyTextProperty, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Needlework.Net.Desktop/Controls/Card.axaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<Styles xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:controls="using:Needlework.Net.Desktop.Controls">
|
||||||
|
<Design.PreviewWith>
|
||||||
|
<controls:Card />
|
||||||
|
</Design.PreviewWith>
|
||||||
|
|
||||||
|
<Style Selector="controls|Card">
|
||||||
|
<!-- Set Defaults -->
|
||||||
|
<Setter Property="Template">
|
||||||
|
<ControlTemplate>
|
||||||
|
<Border Padding="16"
|
||||||
|
CornerRadius="16,16,16,16"
|
||||||
|
Background="{DynamicResource CardBackgroundFillColorDefaultBrush}">
|
||||||
|
<ContentPresenter Content="{TemplateBinding Content}"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
10
Needlework.Net.Desktop/Controls/Card.axaml.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Desktop.Controls;
|
||||||
|
|
||||||
|
public class Card : ContentControl
|
||||||
|
{
|
||||||
|
public Card()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Needlework.Net.Desktop/GithubRelease.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Desktop
|
||||||
|
{
|
||||||
|
public class GithubRelease
|
||||||
|
{
|
||||||
|
[JsonPropertyName("tag_name")]
|
||||||
|
public string TagName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool IsLatest(int version) => int.Parse(TagName.Replace(".", "")) > version;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Needlework.Net.Desktop/Messages/InfoBarUpdateMessage.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using Needlework.Net.Desktop.ViewModels;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Desktop.Messages
|
||||||
|
{
|
||||||
|
public class InfoBarUpdateMessage(InfoBarViewModel vm) : ValueChangedMessage<InfoBarViewModel>(vm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,37 +9,37 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Avalonia">
|
<PropertyGroup Label="Avalonia">
|
||||||
<AvaloniaXamlIlDebuggerLaunch>False</AvaloniaXamlIlDebuggerLaunch>
|
<AvaloniaXamlIlDebuggerLaunch>False</AvaloniaXamlIlDebuggerLaunch>
|
||||||
<ApplicationIcon>app.ico</ApplicationIcon>
|
<ApplicationIcon>app.ico</ApplicationIcon>
|
||||||
<AssemblyName>NeedleworkDotNet</AssemblyName>
|
<AssemblyName>NeedleworkDotNet</AssemblyName>
|
||||||
<AssemblyVersion>0.1.1.0</AssemblyVersion>
|
<AssemblyVersion>0.5.1.0</AssemblyVersion>
|
||||||
<FileVersion>0.1.1.0</FileVersion>
|
<FileVersion>$(AssemblyVersion)</FileVersion>
|
||||||
<AvaloniaXamlVerboseExceptions>False</AvaloniaXamlVerboseExceptions>
|
<AvaloniaXamlVerboseExceptions>False</AvaloniaXamlVerboseExceptions>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="11.1.0-beta2" />
|
<PackageReference Include="Avalonia" Version="11.1.3" />
|
||||||
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.0.6" />
|
<PackageReference Include="Avalonia.AvaloniaEdit" Version="11.1.0" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.0-beta2" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.3" />
|
||||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.1.0-beta2" />
|
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.1.3" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.1.0-beta2" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.1.3" />
|
||||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.1.0-beta2" />
|
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.1.3" />
|
||||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.0-beta2" />
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.3" />
|
||||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.0-beta2" />
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.3" />
|
||||||
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.0.6" />
|
<PackageReference Include="AvaloniaEdit.TextMate" Version="11.1.0" />
|
||||||
<PackageReference Include="BlossomiShymae.GrrrLCU" Version="0.9.0" />
|
<PackageReference Include="BlossomiShymae.GrrrLCU" Version="0.9.0" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||||
|
<PackageReference Include="FluentAvaloniaUI" Version="2.1.0" />
|
||||||
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.10" />
|
<PackageReference Include="Material.Icons.Avalonia" Version="2.1.10" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
||||||
<PackageReference Include="Projektanker.Icons.Avalonia" Version="9.4.0" />
|
<PackageReference Include="Projektanker.Icons.Avalonia" Version="9.4.0" />
|
||||||
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0" />
|
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0" />
|
||||||
<PackageReference Include="SukiUI" Version="6.0.0-beta7" />
|
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.62" />
|
||||||
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.60" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="Assets\**" />
|
<AvaloniaResource Include="Assets\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -51,6 +51,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Update="Controls\BusyArea.axaml.cs">
|
||||||
|
<DependentUpon>BusyArea.axaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Update="Views\EndpointView.axaml.cs">
|
<Compile Update="Views\EndpointView.axaml.cs">
|
||||||
<DependentUpon>EndpointView.axaml</DependentUpon>
|
<DependentUpon>EndpointView.axaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using Needlework.Net.Desktop.ViewModels;
|
using Needlework.Net.Desktop.ViewModels;
|
||||||
using Needlework.Net.Desktop.Views;
|
using Needlework.Net.Desktop.Views;
|
||||||
using SukiUI.Controls;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.Services
|
namespace Needlework.Net.Desktop.Services
|
||||||
{
|
{
|
||||||
@@ -12,9 +10,6 @@ namespace Needlework.Net.Desktop.Services
|
|||||||
{
|
{
|
||||||
public IServiceProvider ServiceProvider { get; }
|
public IServiceProvider ServiceProvider { get; }
|
||||||
|
|
||||||
public Dictionary<string, SukiWindow> EndpointWindows { get; } = []; // Workaround memory leak by storing and reusing windows.
|
|
||||||
// Figure out why creating and closing windows leaks memory.
|
|
||||||
|
|
||||||
public OopsiesWindow? OopsiesWindow { get; set; }
|
public OopsiesWindow? OopsiesWindow { get; set; }
|
||||||
|
|
||||||
public WindowService(IServiceProvider serviceProvider)
|
public WindowService(IServiceProvider serviceProvider)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
public class AboutViewModel : PageBase
|
public class AboutViewModel : PageBase
|
||||||
{
|
{
|
||||||
public AboutViewModel() : base("About", Material.Icons.MaterialIconKind.InfoCircle)
|
public AboutViewModel() : base("About", "info-circle")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,9 @@ using CommunityToolkit.Mvvm.Input;
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using Needlework.Net.Desktop.Services;
|
using Needlework.Net.Desktop.Services;
|
||||||
using SukiUI.Controls;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
@@ -17,10 +15,10 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
public partial class ConsoleViewModel : PageBase, IRecipient<DataReadyMessage>
|
public partial class ConsoleViewModel : PageBase, IRecipient<DataReadyMessage>
|
||||||
{
|
{
|
||||||
public IAvaloniaReadOnlyList<string> RequestMethods { get; } = new AvaloniaList<string>(["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"]);
|
public IAvaloniaReadOnlyList<string> RequestMethods { get; } = new AvaloniaList<string>(["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE"]);
|
||||||
|
public IAvaloniaList<string> RequestPaths { get; } = new AvaloniaList<string>();
|
||||||
|
|
||||||
[ObservableProperty] private bool _isBusy = true;
|
[ObservableProperty] private bool _isBusy = true;
|
||||||
[ObservableProperty] private bool _isRequestBusy = false;
|
[ObservableProperty] private bool _isRequestBusy = false;
|
||||||
[ObservableProperty] private IAvaloniaReadOnlyList<string> _requestPaths = new AvaloniaList<string>();
|
|
||||||
[ObservableProperty] private string? _requestMethodSelected = "GET";
|
[ObservableProperty] private string? _requestMethodSelected = "GET";
|
||||||
[ObservableProperty] private string? _requestPath = null;
|
[ObservableProperty] private string? _requestPath = null;
|
||||||
[ObservableProperty] private string? _requestBody = null;
|
[ObservableProperty] private string? _requestBody = null;
|
||||||
@@ -30,7 +28,7 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
|
|
||||||
public WindowService WindowService { get; }
|
public WindowService WindowService { get; }
|
||||||
|
|
||||||
public ConsoleViewModel(WindowService windowService) : base("Console", Material.Icons.MaterialIconKind.Console, -200)
|
public ConsoleViewModel(WindowService windowService) : base("Console", "terminal", -200)
|
||||||
{
|
{
|
||||||
WindowService = windowService;
|
WindowService = windowService;
|
||||||
|
|
||||||
@@ -60,22 +58,26 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
|
|
||||||
var processInfo = Connector.GetProcessInfo();
|
var processInfo = Connector.GetProcessInfo();
|
||||||
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "ConsoleRequestEditor").Response;
|
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "ConsoleRequestEditor").Response;
|
||||||
var content = new StringContent(Regex.Replace(requestBody, @"\s+", ""), new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
||||||
var response = await Connector.SendAsync(method, RequestPath, content) ?? throw new Exception("Response is null.");
|
var response = await Connector.SendAsync(method, RequestPath, content);
|
||||||
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
|
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
|
||||||
var body = await response.Content.ReadAsStringAsync();
|
var responseBody = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
|
||||||
body = !string.IsNullOrEmpty(body) ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(body), App.JsonSerializerOptions) : string.Empty;
|
var body = responseBody.Length > 0 ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBody), App.JsonSerializerOptions) : string.Empty;
|
||||||
if (body.Length >= App.MaxCharacters) WindowService.ShowOopsiesWindow(body);
|
if (body.Length >= App.MaxCharacters)
|
||||||
|
{
|
||||||
|
WindowService.ShowOopsiesWindow(body);
|
||||||
|
WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(string.Empty), nameof(ConsoleViewModel));
|
||||||
|
}
|
||||||
else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(body), nameof(ConsoleViewModel));
|
else WeakReferenceMessenger.Default.Send(new ResponseUpdatedMessage(body), nameof(ConsoleViewModel));
|
||||||
|
|
||||||
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode.ToString()}";
|
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode.ToString()}";
|
||||||
ResponsePath = $"https://127.0.0.1:{processInfo.AppPort}{RequestPath}";
|
ResponsePath = $"https://127.0.0.1:{processInfo.AppPort}{RequestPath}";
|
||||||
ResponseAuthorization = $"Basic {riotAuthentication.Value}";
|
ResponseAuthorization = $"Basic {riotAuthentication.Value}";
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await SukiHost.ShowToast("Request Failed", ex.Message, SukiUI.Enums.NotificationType.Error);
|
WeakReferenceMessenger.Default.Send(new InfoBarUpdateMessage(new InfoBarViewModel("Request Failed", true, ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error, TimeSpan.FromSeconds(5))));
|
||||||
ResponseStatus = null;
|
ResponseStatus = null;
|
||||||
ResponsePath = null;
|
ResponsePath = null;
|
||||||
ResponseAuthorization = null;
|
ResponseAuthorization = null;
|
||||||
@@ -91,7 +93,8 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
{
|
{
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Invoke(() =>
|
Avalonia.Threading.Dispatcher.UIThread.Invoke(() =>
|
||||||
{
|
{
|
||||||
RequestPaths = new AvaloniaList<string>([.. message.Value.Paths]);
|
RequestPaths.Clear();
|
||||||
|
RequestPaths.AddRange(message.Value.Paths);
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,49 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using SukiUI.Controls;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
{
|
{
|
||||||
public partial class EndpointViewModel : ObservableObject, ISukiStackPageTitleProvider
|
public partial class EndpointViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
public string Endpoint { get; }
|
public string Endpoint { get; }
|
||||||
public string Title => Endpoint;
|
public string Title => Endpoint;
|
||||||
|
|
||||||
[ObservableProperty] private IAvaloniaReadOnlyList<PathOperationViewModel> _pathOperations;
|
|
||||||
|
public IAvaloniaReadOnlyList<PathOperationViewModel> PathOperations { get; }
|
||||||
[ObservableProperty] private PathOperationViewModel? _selectedPathOperation;
|
[ObservableProperty] private PathOperationViewModel? _selectedPathOperation;
|
||||||
|
|
||||||
|
[ObservableProperty] private string? _search;
|
||||||
|
public IAvaloniaList<PathOperationViewModel> FilteredPathOperations { get; }
|
||||||
|
|
||||||
public EndpointViewModel(string endpoint)
|
public EndpointViewModel(string endpoint)
|
||||||
{
|
{
|
||||||
Endpoint = endpoint;
|
Endpoint = endpoint;
|
||||||
|
|
||||||
var handler = WeakReferenceMessenger.Default.Send<DataRequestMessage>().Response;
|
var handler = WeakReferenceMessenger.Default.Send<DataRequestMessage>().Response;
|
||||||
PathOperations = new AvaloniaList<PathOperationViewModel>(handler.Plugins[endpoint].Select(x => new PathOperationViewModel(x)));
|
PathOperations = new AvaloniaList<PathOperationViewModel>(handler.Plugins[endpoint].Select(x => new PathOperationViewModel(x)));
|
||||||
|
FilteredPathOperations = new AvaloniaList<PathOperationViewModel>(PathOperations);
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSearchChanged(string? value)
|
||||||
|
{
|
||||||
|
FilteredPathOperations.Clear();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
FilteredPathOperations.AddRange(PathOperations);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FilteredPathOperations.AddRange(PathOperations.Where(o => o.Path.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedPathOperationChanged(PathOperationViewModel? value)
|
||||||
|
{
|
||||||
|
if (value == null) return;
|
||||||
|
WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(value.Operation.RequestTemplate ?? string.Empty, "EndpointRequestEditor")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,31 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using SukiUI.Controls;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
{
|
{
|
||||||
public partial class EndpointsContainerViewModel : PageBase
|
public partial class EndpointsContainerViewModel : PageBase
|
||||||
{
|
{
|
||||||
[ObservableProperty] private ISukiStackPageTitleProvider _activeViewModel;
|
[ObservableProperty] private ObservableObject _activeViewModel;
|
||||||
|
[ObservableProperty] private ObservableObject _endpointsViewModel;
|
||||||
|
[ObservableProperty] private string _title = string.Empty;
|
||||||
|
|
||||||
public EndpointsContainerViewModel(HttpClient httpClient) : base("Endpoints", Material.Icons.MaterialIconKind.Hub, -500)
|
public EndpointsContainerViewModel(HttpClient httpClient) : base("Endpoints", "list-alt", -500)
|
||||||
{
|
{
|
||||||
_activeViewModel = new EndpointsViewModel(httpClient, OnClicked);
|
_activeViewModel = _endpointsViewModel = new EndpointsViewModel(httpClient, OnClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClicked(ISukiStackPageTitleProvider viewModel)
|
private void OnClicked(ObservableObject viewModel)
|
||||||
{
|
{
|
||||||
ActiveViewModel = viewModel;
|
ActiveViewModel = viewModel;
|
||||||
|
if (viewModel is EndpointViewModel endpoint) Title = endpoint.Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void GoBack()
|
||||||
|
{
|
||||||
|
ActiveViewModel = EndpointsViewModel;
|
||||||
|
Title = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using SukiUI.Controls;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
{
|
{
|
||||||
public partial class EndpointsViewModel : ObservableObject, IRecipient<DataReadyMessage>, ISukiStackPageTitleProvider
|
public partial class EndpointsViewModel : ObservableObject, IRecipient<DataReadyMessage>
|
||||||
{
|
{
|
||||||
public HttpClient HttpClient { get; }
|
public HttpClient HttpClient { get; }
|
||||||
|
|
||||||
public string Title => "Endpoints";
|
public string Title => "Endpoints";
|
||||||
public Action<ISukiStackPageTitleProvider> OnClicked;
|
public Action<ObservableObject> OnClicked;
|
||||||
|
public IAvaloniaList<string> Plugins { get; } = new AvaloniaList<string>();
|
||||||
|
public IAvaloniaList<string> Query { get; } = new AvaloniaList<string>();
|
||||||
|
|
||||||
[ObservableProperty] private IAvaloniaReadOnlyList<string> _plugins = new AvaloniaList<string>();
|
|
||||||
[ObservableProperty] private bool _isBusy = true;
|
[ObservableProperty] private bool _isBusy = true;
|
||||||
[ObservableProperty] private string _search = string.Empty;
|
[ObservableProperty] private string _search = string.Empty;
|
||||||
[ObservableProperty] private IAvaloniaReadOnlyList<string> _query = new AvaloniaList<string>();
|
|
||||||
[ObservableProperty] private string? _selectedQuery = string.Empty;
|
[ObservableProperty] private string? _selectedQuery = string.Empty;
|
||||||
|
|
||||||
public EndpointsViewModel(HttpClient httpClient, Action<ISukiStackPageTitleProvider> onClicked)
|
public EndpointsViewModel(HttpClient httpClient, Action<ObservableObject> onClicked)
|
||||||
{
|
{
|
||||||
HttpClient = httpClient;
|
HttpClient = httpClient;
|
||||||
OnClicked = onClicked;
|
OnClicked = onClicked;
|
||||||
@@ -33,19 +33,23 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
public void Receive(DataReadyMessage message)
|
public void Receive(DataReadyMessage message)
|
||||||
{
|
{
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
Plugins = new AvaloniaList<string>([.. message.Value.Plugins.Keys]);
|
Plugins.Clear();
|
||||||
Query = new AvaloniaList<string>([.. Plugins]);
|
Plugins.AddRange(message.Value.Plugins.Keys);
|
||||||
|
Query.Clear();
|
||||||
|
Query.AddRange(Plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSearchChanged(string value)
|
partial void OnSearchChanged(string value)
|
||||||
{
|
{
|
||||||
|
Query.Clear();
|
||||||
if (!string.IsNullOrEmpty(Search))
|
if (!string.IsNullOrEmpty(Search))
|
||||||
Query = new AvaloniaList<string>(Plugins.Where(x => x.Contains(value)));
|
Query.AddRange(Plugins.Where(x => x.Contains(value, StringComparison.InvariantCultureIgnoreCase)));
|
||||||
else
|
else
|
||||||
Query = Plugins;
|
Query.AddRange(Plugins);
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSelectedQueryChanged(string? value)
|
[RelayCommand]
|
||||||
|
private void OpenEndpoint(string? value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(value)) return;
|
if (string.IsNullOrEmpty(value)) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +1,18 @@
|
|||||||
using Avalonia.Media;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using BlossomiShymae.GrrrLCU;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
|
||||||
using CommunityToolkit.Mvvm.Input;
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
{
|
{
|
||||||
public partial class HomeViewModel : PageBase
|
public partial class HomeViewModel : PageBase
|
||||||
{
|
{
|
||||||
[ObservableProperty] private string _statusText = string.Empty;
|
public HomeViewModel() : base("Home", "home", int.MinValue) { }
|
||||||
[ObservableProperty] private IBrush? _statusForeground;
|
|
||||||
[ObservableProperty] private string _statusAddress = string.Empty;
|
|
||||||
|
|
||||||
public HomeViewModel() : base("Home", Material.Icons.MaterialIconKind.Home, int.MinValue)
|
|
||||||
{
|
|
||||||
var thread = new Thread(StartProcessing) { IsBackground = true };
|
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartProcessing()
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
void Set(string text, Color color, string address)
|
|
||||||
{
|
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Invoke(() =>
|
|
||||||
{
|
|
||||||
StatusText = text;
|
|
||||||
StatusForeground = new SolidColorBrush(color.ToUInt32());
|
|
||||||
StatusAddress = address;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var processInfo = Connector.GetProcessInfo();
|
|
||||||
Set("Online", Colors.Green, $"https://127.0.0.1:{processInfo.AppPort}/");
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException)
|
|
||||||
{
|
|
||||||
Set("Offline", Colors.Red, "N/A");
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.Sleep(TimeSpan.FromSeconds(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenUrl(string url)
|
private void OpenUrl(string url)
|
||||||
{
|
{
|
||||||
var process = new Process()
|
var process = new Process()
|
||||||
{
|
{
|
||||||
StartInfo = new ProcessStartInfo(url)
|
StartInfo = new ProcessStartInfo(url) { UseShellExecute = true }
|
||||||
{
|
|
||||||
UseShellExecute = true
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
process.Start();
|
process.Start();
|
||||||
}
|
}
|
||||||
|
|||||||
27
Needlework.Net.Desktop/ViewModels/InfoBarViewModel.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
|
{
|
||||||
|
public partial class InfoBarViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty] private string _title;
|
||||||
|
[ObservableProperty] private bool _isOpen;
|
||||||
|
[ObservableProperty] private string _message;
|
||||||
|
[ObservableProperty] private InfoBarSeverity _severity;
|
||||||
|
[ObservableProperty] private TimeSpan _duration;
|
||||||
|
[ObservableProperty] private Control? _actionButton;
|
||||||
|
|
||||||
|
public InfoBarViewModel(string title, bool isOpen, string message, InfoBarSeverity severity, TimeSpan duration, Control? actionButton = null)
|
||||||
|
{
|
||||||
|
_title = title;
|
||||||
|
_isOpen = isOpen;
|
||||||
|
_message = message;
|
||||||
|
_severity = severity;
|
||||||
|
_duration = duration;
|
||||||
|
_actionButton = actionButton;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,25 +2,33 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Needlework.Net.Core;
|
using Needlework.Net.Core;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using Needlework.Net.Desktop.Services;
|
using Needlework.Net.Desktop.Services;
|
||||||
using SukiUI.Controls;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
{
|
{
|
||||||
public partial class MainWindowViewModel : ObservableObject, IRecipient<DataRequestMessage>, IRecipient<HostDocumentRequestMessage>, IRecipient<OopsiesWindowRequestedMessage>
|
public partial class MainWindowViewModel : ObservableObject, IRecipient<DataRequestMessage>, IRecipient<HostDocumentRequestMessage>, IRecipient<OopsiesWindowRequestedMessage>, IRecipient<InfoBarUpdateMessage>
|
||||||
{
|
{
|
||||||
public IAvaloniaReadOnlyList<PageBase> Pages { get; }
|
public IAvaloniaReadOnlyList<NavigationViewItem> MenuItems { get; }
|
||||||
|
[NotifyPropertyChangedFor(nameof(CurrentPage))]
|
||||||
|
[ObservableProperty] private NavigationViewItem _selectedMenuItem;
|
||||||
|
public PageBase CurrentPage => (PageBase)SelectedMenuItem.Tag!;
|
||||||
|
|
||||||
public string Version { get; } = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0";
|
public string Version { get; } = Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "0.0.0.0";
|
||||||
|
[ObservableProperty] private bool _isUpdateShown = false;
|
||||||
|
|
||||||
public HttpClient HttpClient { get; }
|
public HttpClient HttpClient { get; }
|
||||||
public WindowService WindowService { get; }
|
public WindowService WindowService { get; }
|
||||||
@@ -29,14 +37,68 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
|
|
||||||
[ObservableProperty] private bool _isBusy = true;
|
[ObservableProperty] private bool _isBusy = true;
|
||||||
|
|
||||||
|
[ObservableProperty] private ObservableCollection<InfoBarViewModel> _infoBarItems = [];
|
||||||
|
|
||||||
public MainWindowViewModel(IEnumerable<PageBase> pages, HttpClient httpClient, WindowService windowService)
|
public MainWindowViewModel(IEnumerable<PageBase> pages, HttpClient httpClient, WindowService windowService)
|
||||||
{
|
{
|
||||||
Pages = new AvaloniaList<PageBase>(pages.OrderBy(x => x.Index).ThenBy(x => x.DisplayName));
|
MenuItems = new AvaloniaList<NavigationViewItem>(pages
|
||||||
|
.OrderBy(p => p.Index)
|
||||||
|
.ThenBy(p => p.DisplayName)
|
||||||
|
.Select(p => new NavigationViewItem()
|
||||||
|
{
|
||||||
|
Content = p.DisplayName,
|
||||||
|
Tag = p,
|
||||||
|
IconSource = new BitmapIconSource() { UriSource = new Uri($"avares://NeedleworkDotNet/Assets/Icons/{p.Icon}.png") }
|
||||||
|
}));
|
||||||
|
SelectedMenuItem = MenuItems[0];
|
||||||
|
|
||||||
HttpClient = httpClient;
|
HttpClient = httpClient;
|
||||||
WindowService = windowService;
|
WindowService = windowService;
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.RegisterAll(this);
|
WeakReferenceMessenger.Default.RegisterAll(this);
|
||||||
|
|
||||||
Task.Run(FetchDataAsync);
|
Task.Run(FetchDataAsync);
|
||||||
|
new Thread(ProcessEvents) { IsBackground = true }.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessEvents(object? obj)
|
||||||
|
{
|
||||||
|
while (!IsUpdateShown)
|
||||||
|
{
|
||||||
|
Task.Run(CheckLatestVersionAsync);
|
||||||
|
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(60));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckLatestVersionAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/BlossomiShymae/Needlework.Net/releases/latest");
|
||||||
|
request.Headers.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue("Needlework.Net", Version));
|
||||||
|
|
||||||
|
var response = await HttpClient.SendAsync(request);
|
||||||
|
var release = await response.Content.ReadFromJsonAsync<GithubRelease>();
|
||||||
|
if (release == null) return;
|
||||||
|
|
||||||
|
var currentVersion = int.Parse(Version.Replace(".", ""));
|
||||||
|
|
||||||
|
if (release.IsLatest(currentVersion))
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(async () =>
|
||||||
|
{
|
||||||
|
await ShowInfoBarAsync(new("Needlework.Net Update", true, $"There is a new version available: {release.TagName}.", InfoBarSeverity.Informational, TimeSpan.FromSeconds(10), new Avalonia.Controls.Button()
|
||||||
|
{
|
||||||
|
Command = OpenUrlCommand,
|
||||||
|
CommandParameter = "https://github.com/BlossomiShymae/Needlework.Net/releases",
|
||||||
|
Content = "Download"
|
||||||
|
}));
|
||||||
|
IsUpdateShown = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task FetchDataAsync()
|
private async Task FetchDataAsync()
|
||||||
@@ -47,7 +109,6 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
LcuSchemaHandler = handler;
|
LcuSchemaHandler = handler;
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.Send(new DataReadyMessage(handler));
|
WeakReferenceMessenger.Default.Send(new DataReadyMessage(handler));
|
||||||
await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(async () => await SukiHost.ShowToast("OpenAPI Data Processed", "Some pages can now be used.", SukiUI.Enums.NotificationType.Success, TimeSpan.FromSeconds(5)));
|
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,15 +135,21 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
process.Start();
|
process.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private void OpenConsole()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Receive(OopsiesWindowRequestedMessage message)
|
public void Receive(OopsiesWindowRequestedMessage message)
|
||||||
{
|
{
|
||||||
WindowService.ShowOopsiesWindow(message.Value);
|
WindowService.ShowOopsiesWindow(message.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Receive(InfoBarUpdateMessage message)
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(async () => await ShowInfoBarAsync(message.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ShowInfoBarAsync(InfoBarViewModel vm)
|
||||||
|
{
|
||||||
|
InfoBarItems.Add(vm);
|
||||||
|
await Task.Delay(vm.Duration);
|
||||||
|
InfoBarItems.Remove(vm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using CommunityToolkit.Mvvm.Messaging;
|
|||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
{
|
{
|
||||||
@@ -18,6 +20,7 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
public IAvaloniaReadOnlyList<PropertyClassViewModel> ResponseClasses { get; }
|
public IAvaloniaReadOnlyList<PropertyClassViewModel> ResponseClasses { get; }
|
||||||
public IAvaloniaReadOnlyList<ParameterViewModel> PathParameters { get; }
|
public IAvaloniaReadOnlyList<ParameterViewModel> PathParameters { get; }
|
||||||
public IAvaloniaReadOnlyList<ParameterViewModel> QueryParameters { get; }
|
public IAvaloniaReadOnlyList<ParameterViewModel> QueryParameters { get; }
|
||||||
|
public string? RequestTemplate { get; }
|
||||||
|
|
||||||
public OperationViewModel(OpenApiOperation operation)
|
public OperationViewModel(OpenApiOperation operation)
|
||||||
{
|
{
|
||||||
@@ -30,6 +33,80 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
PathParameters = GetParameters(operation.Parameters, ParameterLocation.Path);
|
PathParameters = GetParameters(operation.Parameters, ParameterLocation.Path);
|
||||||
QueryParameters = GetParameters(operation.Parameters, ParameterLocation.Query);
|
QueryParameters = GetParameters(operation.Parameters, ParameterLocation.Query);
|
||||||
RequestBodyType = GetRequestBodyType(operation.RequestBody);
|
RequestBodyType = GetRequestBodyType(operation.RequestBody);
|
||||||
|
RequestTemplate = GetRequestTemplate(operation.RequestBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetRequestTemplate(OpenApiRequestBody? requestBody)
|
||||||
|
{
|
||||||
|
var requestClasses = GetRequestClasses(requestBody);
|
||||||
|
if (requestClasses.Count == 0)
|
||||||
|
{
|
||||||
|
var type = GetRequestBodyType(requestBody);
|
||||||
|
if (type == null) return null;
|
||||||
|
return GetRequestDefaultValue(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
var template = CreateTemplate(requestClasses);
|
||||||
|
return JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(string.Join(string.Empty, template)), App.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> CreateTemplate(AvaloniaList<PropertyClassViewModel> requestClasses)
|
||||||
|
{
|
||||||
|
if (requestClasses.Count == 0) return [];
|
||||||
|
List<string> template = [];
|
||||||
|
template.Add("{");
|
||||||
|
|
||||||
|
var rootClass = requestClasses.First();
|
||||||
|
if (rootClass.PropertyEnums.Any()) return [rootClass.PropertyEnums.First().Values];
|
||||||
|
var propertyFields = rootClass.PropertyFields;
|
||||||
|
for (int i = 0; i < propertyFields.Count; i++)
|
||||||
|
{
|
||||||
|
template.Add($"\"{propertyFields[i].Name}\"");
|
||||||
|
template.Add(":");
|
||||||
|
template.Add($"#{propertyFields[i].Type}");
|
||||||
|
|
||||||
|
if (i == propertyFields.Count - 1) template.Add("}");
|
||||||
|
else template.Add(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < template.Count; i++)
|
||||||
|
{
|
||||||
|
var type = template[i];
|
||||||
|
if (!type.Contains("#")) continue;
|
||||||
|
|
||||||
|
var foundClass = requestClasses.Where(c => c.Id == type.Replace("#", string.Empty));
|
||||||
|
if (foundClass.Any())
|
||||||
|
{
|
||||||
|
if (foundClass.First().PropertyEnums.Any())
|
||||||
|
{
|
||||||
|
template[i] = string.Join(string.Empty, CreateTemplate([.. foundClass]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AvaloniaList<PropertyClassViewModel> classes = [.. requestClasses];
|
||||||
|
classes.Remove(rootClass);
|
||||||
|
template[i] = string.Join(string.Empty, CreateTemplate(classes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
template[i] = GetRequestDefaultValue(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRequestDefaultValue(string type)
|
||||||
|
{
|
||||||
|
var defaultValue = string.Empty;
|
||||||
|
if (type.Contains("[]")) defaultValue = "[]";
|
||||||
|
else if (type.Contains("string")) defaultValue = "\"\"";
|
||||||
|
else if (type.Contains("boolean")) defaultValue = "false";
|
||||||
|
else if (type.Contains("integer")) defaultValue = "0";
|
||||||
|
else if (type.Contains("double") || type.Contains("float")) defaultValue = "0.0";
|
||||||
|
else if (type.Contains("object")) defaultValue = "{}";
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? GetRequestBodyType(OpenApiRequestBody? requestBody)
|
private string? GetRequestBodyType(OpenApiRequestBody? requestBody)
|
||||||
@@ -38,6 +115,7 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
if (requestBody.Content.TryGetValue("application/json", out var media))
|
if (requestBody.Content.TryGetValue("application/json", out var media))
|
||||||
{
|
{
|
||||||
var schema = media.Schema;
|
var schema = media.Schema;
|
||||||
|
if (schema == null) return null; // Because "PostLolAccountVerificationV1SendDeactivationPin" exists where the media body is empty...
|
||||||
return GetSchemaType(schema);
|
return GetSchemaType(schema);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -48,8 +126,8 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
var pathParameters = new AvaloniaList<ParameterViewModel>();
|
var pathParameters = new AvaloniaList<ParameterViewModel>();
|
||||||
foreach (var parameter in parameters)
|
foreach (var parameter in parameters)
|
||||||
{
|
{
|
||||||
if (parameter.In != location) break;
|
if (parameter.In != location) continue;
|
||||||
pathParameters.Add(new ParameterViewModel(parameter.Name, parameter.Schema.Type, parameter.Required));
|
pathParameters.Add(new ParameterViewModel(parameter.Name, GetSchemaType(parameter.Schema), parameter.Required));
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathParameters;
|
return pathParameters;
|
||||||
@@ -77,6 +155,8 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
string componentId = GetComponentId(schema);
|
string componentId = GetComponentId(schema);
|
||||||
var componentSchema = document.Components.Schemas[componentId];
|
var componentSchema = document.Components.Schemas[componentId];
|
||||||
var responseClass = new PropertyClassViewModel(componentId, componentSchema.Properties, componentSchema.Enum);
|
var responseClass = new PropertyClassViewModel(componentId, componentSchema.Properties, componentSchema.Enum);
|
||||||
|
|
||||||
|
if (propertyClasses.Where(c => c.Id == componentId).Any()) return; // Avoid adding duplicate schemas in classes
|
||||||
propertyClasses.Add(responseClass);
|
propertyClasses.Add(responseClass);
|
||||||
|
|
||||||
foreach ((var _, var property) in componentSchema.Properties)
|
foreach ((var _, var property) in componentSchema.Properties)
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Material.Icons;
|
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
{
|
{
|
||||||
|
|
||||||
public abstract partial class PageBase(string displayName, MaterialIconKind icon, int index = 0) : ObservableValidator
|
public abstract partial class PageBase(string displayName, string icon, int index = 0) : ObservableValidator
|
||||||
{
|
{
|
||||||
[ObservableProperty] private string _displayName = displayName;
|
[ObservableProperty] private string _displayName = displayName;
|
||||||
[ObservableProperty] private MaterialIconKind _icon = icon;
|
[ObservableProperty] private string _icon = icon;
|
||||||
[ObservableProperty] private int _index = index;
|
[ObservableProperty] private int _index = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,10 @@ using CommunityToolkit.Mvvm.Input;
|
|||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Needlework.Net.Core;
|
using Needlework.Net.Core;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using SukiUI.Controls;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.ViewModels
|
namespace Needlework.Net.Desktop.ViewModels
|
||||||
@@ -33,14 +32,18 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
public PathOperationViewModel(PathOperation pathOperation)
|
public PathOperationViewModel(PathOperation pathOperation)
|
||||||
{
|
{
|
||||||
Method = pathOperation.Method.ToUpper();
|
Method = pathOperation.Method.ToUpper();
|
||||||
Color = new SolidColorBrush(GetColor(pathOperation.Method.ToUpper()));
|
Color = new SolidColorBrush(GetColor(Method));
|
||||||
Path = pathOperation.Path;
|
Path = pathOperation.Path;
|
||||||
Operation = new OperationViewModel(pathOperation.Operation);
|
Operation = new OperationViewModel(pathOperation.Operation);
|
||||||
ProcessInfo = GetProcessInfo();
|
ProcessInfo = GetProcessInfo();
|
||||||
ResponsePath = ProcessInfo != null ? $"https://127.0.0.1:{ProcessInfo.AppPort}{Path}" : null;
|
if (ProcessInfo != null)
|
||||||
ResponseUsername = ProcessInfo != null ? new RiotAuthentication(ProcessInfo.RemotingAuthToken).Username : null;
|
{
|
||||||
ResponsePassword = ProcessInfo != null ? new RiotAuthentication(ProcessInfo.RemotingAuthToken).Password : null;
|
ResponsePath = $"https://127.0.0.1:{ProcessInfo.AppPort}{Path}";
|
||||||
ResponseAuthorization = ProcessInfo != null ? $"Basic {new RiotAuthentication(ProcessInfo.RemotingAuthToken).Value}" : null;
|
var riotAuth = new RiotAuthentication(ProcessInfo.RemotingAuthToken);
|
||||||
|
ResponseUsername = riotAuth.Username;
|
||||||
|
ResponsePassword = riotAuth.Password;
|
||||||
|
ResponseAuthorization = $"Basic {riotAuth.Value}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProcessInfo? GetProcessInfo()
|
private ProcessInfo? GetProcessInfo()
|
||||||
@@ -61,7 +64,7 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
{
|
{
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
|
|
||||||
var method = Method.ToUpper() switch
|
var method = Method switch
|
||||||
{
|
{
|
||||||
"GET" => HttpMethod.Get,
|
"GET" => HttpMethod.Get,
|
||||||
"POST" => HttpMethod.Post,
|
"POST" => HttpMethod.Post,
|
||||||
@@ -75,31 +78,37 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
};
|
};
|
||||||
|
|
||||||
var processInfo = Connector.GetProcessInfo();
|
var processInfo = Connector.GetProcessInfo();
|
||||||
var path = Path;
|
var sb = new StringBuilder(Path);
|
||||||
foreach (var pathParameter in Operation.PathParameters)
|
foreach (var pathParameter in Operation.PathParameters)
|
||||||
{
|
{
|
||||||
path = path.Replace($"{{{pathParameter.Name}}}", pathParameter.Value);
|
sb.Replace($"{{{pathParameter.Name}}}", pathParameter.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = "";
|
var firstQueryAdded = false;
|
||||||
foreach (var queryParameter in Operation.QueryParameters)
|
foreach (var queryParameter in Operation.QueryParameters)
|
||||||
{
|
{
|
||||||
if (query.Length != 0 && !string.IsNullOrWhiteSpace(queryParameter.Value))
|
if (!string.IsNullOrWhiteSpace(queryParameter.Value))
|
||||||
query += $"&{queryParameter.Name}={Uri.EscapeDataString(queryParameter.Value)}";
|
{
|
||||||
else if (query.Length == 0 && !string.IsNullOrWhiteSpace(queryParameter.Value))
|
sb.Append(firstQueryAdded ? '&' : '?');
|
||||||
query += $"?{queryParameter.Name}={Uri.EscapeDataString(queryParameter.Value)}";
|
firstQueryAdded = true;
|
||||||
|
sb.Append($"{queryParameter.Name}={Uri.EscapeDataString(queryParameter.Value)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var uri = $"{path}{query}";
|
var uri = sb.ToString();
|
||||||
|
|
||||||
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "EndpointRequestEditor").Response;
|
var requestBody = WeakReferenceMessenger.Default.Send(new ContentRequestMessage(), "EndpointRequestEditor").Response;
|
||||||
var content = new StringContent(Regex.Replace(requestBody, @"\s+", ""), new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
var content = new StringContent(requestBody, new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
|
||||||
|
|
||||||
var response = await Connector.SendAsync(method, $"{uri}", content) ?? throw new Exception("Response is null.");
|
var response = await Connector.SendAsync(method, uri, content);
|
||||||
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
|
var riotAuthentication = new RiotAuthentication(processInfo.RemotingAuthToken);
|
||||||
var responseBody = await response.Content.ReadAsStringAsync();
|
var responseBytes = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
|
||||||
responseBody = !string.IsNullOrEmpty(responseBody) ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBody), App.JsonSerializerOptions) : string.Empty;
|
var responseBody = responseBytes.Length > 0 ? JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responseBytes), App.JsonSerializerOptions) : string.Empty;
|
||||||
if (responseBody.Length >= App.MaxCharacters) WeakReferenceMessenger.Default.Send(new OopsiesWindowRequestedMessage(responseBody));
|
if (responseBody.Length >= App.MaxCharacters)
|
||||||
|
{
|
||||||
|
WeakReferenceMessenger.Default.Send(new OopsiesWindowRequestedMessage(responseBody));
|
||||||
|
WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(string.Empty, "EndpointResponseEditor")));
|
||||||
|
}
|
||||||
else WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(responseBody, "EndpointResponseEditor")));
|
else WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(responseBody, "EndpointResponseEditor")));
|
||||||
|
|
||||||
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode}";
|
ResponseStatus = $"{(int)response.StatusCode} {response.StatusCode}";
|
||||||
@@ -110,7 +119,7 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await SukiHost.ShowToast("Request Failed", ex.Message, SukiUI.Enums.NotificationType.Error);
|
WeakReferenceMessenger.Default.Send(new InfoBarUpdateMessage(new InfoBarViewModel("Request Failed", true, ex.Message, FluentAvalonia.UI.Controls.InfoBarSeverity.Error, TimeSpan.FromSeconds(5))));
|
||||||
WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(string.Empty, "EndpointResponseEditor")));
|
WeakReferenceMessenger.Default.Send(new EditorUpdateMessage(new(string.Empty, "EndpointResponseEditor")));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
|
|
||||||
public PropertyEnumViewModel(IList<IOpenApiAny> enumValue)
|
public PropertyEnumViewModel(IList<IOpenApiAny> enumValue)
|
||||||
{
|
{
|
||||||
Values = $"[{string.Join(", ", enumValue.Select(x => ((OpenApiString)x).Value).ToList())}]";
|
Values = $"[{string.Join(", ", enumValue.Select(x => $"\"{((OpenApiString)x).Value}\"").ToList())}]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Material.Icons;
|
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using Needlework.Net.Desktop.Services;
|
using Needlework.Net.Desktop.Services;
|
||||||
using System;
|
using System;
|
||||||
@@ -18,8 +17,9 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
{
|
{
|
||||||
public partial class WebsocketViewModel : PageBase
|
public partial class WebsocketViewModel : PageBase
|
||||||
{
|
{
|
||||||
[NotifyPropertyChangedFor(nameof(FilteredEventLog))]
|
public ObservableCollection<string> EventLog { get; } = [];
|
||||||
[ObservableProperty] private ObservableCollection<string> _eventLog = [];
|
public SemaphoreSlim EventLogLock { get; } = new(1, 1);
|
||||||
|
|
||||||
[NotifyPropertyChangedFor(nameof(FilteredEventLog))]
|
[NotifyPropertyChangedFor(nameof(FilteredEventLog))]
|
||||||
[ObservableProperty] private string _search = string.Empty;
|
[ObservableProperty] private string _search = string.Empty;
|
||||||
[ObservableProperty] private bool _isAttach = true;
|
[ObservableProperty] private bool _isAttach = true;
|
||||||
@@ -32,12 +32,12 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
|
|
||||||
public WindowService WindowService { get; }
|
public WindowService WindowService { get; }
|
||||||
|
|
||||||
public List<string> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? [.. EventLog] : [.. EventLog.Where(x => x.ToLower().Contains(Search.ToLower()))];
|
public IReadOnlyList<string> FilteredEventLog => string.IsNullOrWhiteSpace(Search) ? EventLog : [.. EventLog.Where(x => x.Contains(Search, StringComparison.InvariantCultureIgnoreCase))];
|
||||||
|
|
||||||
public WebsocketViewModel(WindowService windowService) : base("Event Viewer", MaterialIconKind.Connection, -100)
|
public WebsocketViewModel(WindowService windowService) : base("Event Viewer", "plug", -100)
|
||||||
{
|
{
|
||||||
WindowService = windowService;
|
WindowService = windowService;
|
||||||
|
EventLog.CollectionChanged += (s, e) => OnPropertyChanged(nameof(FilteredEventLog));
|
||||||
var thread = new Thread(InitializeWebsocket) { IsBackground = true };
|
var thread = new Thread(InitializeWebsocket) { IsBackground = true };
|
||||||
thread.Start();
|
thread.Start();
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,8 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void Clear()
|
private void Clear()
|
||||||
{
|
{
|
||||||
EventLog = [];
|
_events.Clear();
|
||||||
|
EventLog.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnSelectedEventLogChanged(string? value)
|
partial void OnSelectedEventLogChanged(string? value)
|
||||||
@@ -95,30 +96,35 @@ namespace Needlework.Net.Desktop.ViewModels
|
|||||||
|
|
||||||
private void OnMessage(EventMessage message)
|
private void OnMessage(EventMessage message)
|
||||||
{
|
{
|
||||||
Avalonia.Threading.Dispatcher.UIThread.Invoke(() =>
|
Avalonia.Threading.Dispatcher.UIThread.Invoke(async () =>
|
||||||
{
|
{
|
||||||
if (!IsAttach) return;
|
if (!IsAttach) return;
|
||||||
|
|
||||||
var line = $"{DateTime.Now:HH:mm:ss.fff} {message.Data?.EventType.ToUpper()} {message.Data?.Uri}";
|
var line = $"{DateTime.Now:HH:mm:ss.fff} {message.Data?.EventType.ToUpper()} {message.Data?.Uri}";
|
||||||
var log = EventLog.ToList();
|
|
||||||
Trace.WriteLine($"Message: {line}");
|
Trace.WriteLine($"Message: {line}");
|
||||||
if (log.Count < 1000)
|
|
||||||
{
|
|
||||||
log.Add(line);
|
|
||||||
_events[line] = message;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var key = $"{log[0]}";
|
|
||||||
log.RemoveAt(0);
|
|
||||||
_events.Remove(key);
|
|
||||||
|
|
||||||
log.Add(line);
|
await EventLogLock.WaitAsync();
|
||||||
_events[line] = message;
|
try
|
||||||
}
|
{
|
||||||
|
if (EventLog.Count < 1000)
|
||||||
|
{
|
||||||
|
EventLog.Add(line);
|
||||||
|
_events[line] = message;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var key = EventLog[0];
|
||||||
|
EventLog.RemoveAt(0);
|
||||||
|
_events.Remove(key);
|
||||||
|
|
||||||
EventLog = []; // This is a hack needed to update for ListBox
|
EventLog.Add(line);
|
||||||
EventLog = new ObservableCollection<string>(log);
|
_events[line] = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
EventLogLock.Release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,39 +2,37 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
|
||||||
xmlns:theme="clr-namespace:SukiUI.Theme;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
|
xmlns:controls="using:Needlework.Net.Desktop.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Desktop.Views.AboutView"
|
x:Class="Needlework.Net.Desktop.Views.AboutView"
|
||||||
x:DataType="vm:AboutViewModel">
|
x:DataType="vm:AboutViewModel">
|
||||||
<Grid Margin="8"
|
<Grid Margin="8"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
HorizontalAlignment="Center">
|
HorizontalAlignment="Center">
|
||||||
<WrapPanel
|
<WrapPanel Orientation="Horizontal">
|
||||||
theme:WrapPanelExtensions.AnimatedScroll="true"
|
<controls:Card Margin="8">
|
||||||
Orientation="Horizontal">
|
|
||||||
<suki:GlassCard Margin="8">
|
|
||||||
<Image Source="/Assets/about.png"
|
<Image Source="/Assets/about.png"
|
||||||
RenderOptions.BitmapInterpolationMode="MediumQuality"
|
RenderOptions.BitmapInterpolationMode="MediumQuality"
|
||||||
Width="200"
|
Width="200"
|
||||||
Height="200"/>
|
Height="200"/>
|
||||||
</suki:GlassCard>
|
</controls:Card>
|
||||||
<StackPanel>
|
<StackPanel Margin="8 0 0 0">
|
||||||
<suki:GlassCard Width="400" Margin="8">
|
<controls:Card Width="400" Margin="8">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Classes="h3">Blossomi Shymae</TextBlock>
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">Blossomi Shymae</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</suki:GlassCard>
|
</controls:Card>
|
||||||
<suki:GlassCard Width="400" Margin="8">
|
<controls:Card Width="400" Margin="8">
|
||||||
<suki:GroupBox Header="About">
|
<StackPanel >
|
||||||
|
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">About</TextBlock>
|
||||||
<TextBlock TextWrapping="Wrap">
|
<TextBlock TextWrapping="Wrap">
|
||||||
Needlework.Net is .NET rewrite of Needlework. Like Needlework, this project is inspired by
|
Needlework.Net is the .NET rewrite of Needlework. This tool was made to help others with LCU development. Feel free to ask any questions
|
||||||
LCU Explorer. This tool was made to help others with LCU development. Feel free to ask any questions
|
or help contribute to the project! Made with love. 💜
|
||||||
or help contribute to the project! 💜
|
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</suki:GroupBox>
|
</StackPanel>
|
||||||
</suki:GlassCard>
|
</controls:Card>
|
||||||
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -2,85 +2,86 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
|
||||||
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
||||||
xmlns:theme="clr-namespace:SukiUI.Theme;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
|
xmlns:controls="using:Needlework.Net.Desktop.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Desktop.Views.ConsoleView"
|
x:Class="Needlework.Net.Desktop.Views.ConsoleView"
|
||||||
x:DataType="vm:ConsoleViewModel">
|
x:DataType="vm:ConsoleViewModel">
|
||||||
<suki:BusyArea IsBusy="{Binding IsBusy}" BusyText="Loading...">
|
<controls:BusyArea IsBusy="{Binding IsBusy}"
|
||||||
|
BusyText="Loading...">
|
||||||
<Grid Margin="16" RowDefinitions="auto,*" ColumnDefinitions="*,*">
|
<Grid Margin="16" RowDefinitions="auto,*" ColumnDefinitions="*,*">
|
||||||
<Grid Grid.Row="0"
|
<Grid Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="2">
|
Grid.ColumnSpan="2">
|
||||||
<suki:GlassCard Margin="0 0 0 16">
|
<StackPanel Margin="0 0 0 16">
|
||||||
<suki:GroupBox Header="Console">
|
<Grid RowDefinitions="auto" ColumnDefinitions="auto,*,auto">
|
||||||
<Grid RowDefinitions="auto,auto" ColumnDefinitions="auto,*">
|
<ComboBox ItemsSource="{Binding RequestMethods}"
|
||||||
<ComboBox ItemsSource="{Binding RequestMethods}" SelectedItem="{Binding RequestMethodSelected}"
|
SelectedItem="{Binding RequestMethodSelected}"
|
||||||
Grid.Row="0" Grid.Column="0"/>
|
Margin="0 0 8 0"
|
||||||
<AutoCompleteBox
|
Grid.Row="0"
|
||||||
ItemsSource="{Binding RequestPaths}"
|
Grid.Column="0"/>
|
||||||
Text="{Binding RequestPath}"
|
<AutoCompleteBox
|
||||||
MaxDropDownHeight="400"
|
ItemsSource="{Binding RequestPaths}"
|
||||||
FilterMode="StartsWith"
|
Text="{Binding RequestPath}"
|
||||||
Grid.Row="0" Grid.Column="1"/>
|
MaxDropDownHeight="400"
|
||||||
<avaloniaEdit:TextEditor
|
FilterMode="StartsWith"
|
||||||
Name="RequestEditor"
|
Grid.Row="0"
|
||||||
Text=""
|
Grid.Column="1"/>
|
||||||
ShowLineNumbers="True"
|
<Button Margin="8 0 0 0"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalAlignment="Center"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalAlignment="Bottom"
|
||||||
FontSize="12"
|
FontWeight="DemiBold"
|
||||||
Height="100"
|
Grid.Row="0"
|
||||||
Grid.Row="1"
|
Grid.Column="2"
|
||||||
Grid.Column="0"
|
Command="{Binding SendRequestCommand}">
|
||||||
Grid.ColumnSpan="2"/>
|
Send
|
||||||
</Grid>
|
</Button>
|
||||||
</suki:GroupBox>
|
</Grid>
|
||||||
</suki:GlassCard>
|
</StackPanel>
|
||||||
<Button Classes="Flat Rounded"
|
|
||||||
Margin="0 0 0 0"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Bottom"
|
|
||||||
FontWeight="DemiBold"
|
|
||||||
Command="{Binding SendRequestCommand}"
|
|
||||||
theme:ButtonExtensions.ShowProgress="{Binding IsRequestBusy}">
|
|
||||||
Send
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackPanel
|
<Grid Grid.Row="1"
|
||||||
Margin="0 0 8 0"
|
Grid.Column="0"
|
||||||
Grid.Row="1"
|
RowDefinitions="auto,*"
|
||||||
Grid.Column="0">
|
ColumnDefinitions="*">
|
||||||
<suki:GlassCard Margin="0 4">
|
<TextBox IsReadOnly="True"
|
||||||
<suki:GroupBox Header="Path">
|
Grid.Row="0"
|
||||||
<TextBlock Text="{Binding ResponsePath}"/>
|
Grid.Column="0"
|
||||||
</suki:GroupBox>
|
Text="{Binding ResponsePath}"/>
|
||||||
</suki:GlassCard>
|
<avaloniaEdit:TextEditor
|
||||||
<suki:GlassCard Margin="0 4">
|
Name="RequestEditor"
|
||||||
<suki:GroupBox Header="Status">
|
Text=""
|
||||||
<TextBlock Text="{Binding ResponseStatus}"/>
|
ShowLineNumbers="True"
|
||||||
</suki:GroupBox>
|
HorizontalScrollBarVisibility="Auto"
|
||||||
</suki:GlassCard>
|
VerticalScrollBarVisibility="Visible"
|
||||||
<suki:GlassCard Margin="0 4">
|
Margin="0 8 0 0"
|
||||||
<suki:GroupBox Header="Authorization">
|
FontSize="12"
|
||||||
<TextBlock Text="{Binding ResponseAuthorization}" />
|
Grid.Row="1"
|
||||||
</suki:GroupBox>
|
Grid.Column="0"/>
|
||||||
</suki:GlassCard>
|
</Grid>
|
||||||
</StackPanel>
|
<Grid RowDefinitions="35,*"
|
||||||
<suki:GlassCard
|
ColumnDefinitions="*"
|
||||||
Margin="0 8"
|
Margin="8 0 0 0"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1">
|
Grid.Column="1">
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0">
|
||||||
|
<Button Content="{Binding ResponseStatus}"
|
||||||
|
FontSize="12"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
<avaloniaEdit:TextEditor
|
<avaloniaEdit:TextEditor
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
Name="ResponseEditor"
|
Name="ResponseEditor"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
ShowLineNumbers="True"
|
ShowLineNumbers="True"
|
||||||
|
IsReadOnly="True"
|
||||||
Text=""
|
Text=""
|
||||||
FontSize="12"/>
|
FontSize="12"/>
|
||||||
</suki:GlassCard>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</suki:BusyArea>
|
</controls:BusyArea>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using AvaloniaEdit;
|
using AvaloniaEdit;
|
||||||
using AvaloniaEdit.TextMate;
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Needlework.Net.Desktop.Extensions;
|
using Needlework.Net.Desktop.Extensions;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using Needlework.Net.Desktop.ViewModels;
|
using Needlework.Net.Desktop.ViewModels;
|
||||||
using SukiUI;
|
|
||||||
using TextMateSharp.Grammars;
|
using TextMateSharp.Grammars;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.Views;
|
namespace Needlework.Net.Desktop.Views;
|
||||||
@@ -46,7 +44,6 @@ public partial class ConsoleView : UserControl, IRecipient<ResponseUpdatedMessag
|
|||||||
WeakReferenceMessenger.Default.Register<ContentRequestMessage, string>(this, "ConsoleRequestEditor");
|
WeakReferenceMessenger.Default.Register<ContentRequestMessage, string>(this, "ConsoleRequestEditor");
|
||||||
|
|
||||||
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
||||||
SukiTheme.GetInstance().OnBaseThemeChanged += OnBaseThemeChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
@@ -54,19 +51,11 @@ public partial class ConsoleView : UserControl, IRecipient<ResponseUpdatedMessag
|
|||||||
base.OnDetachedFromVisualTree(e);
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||||
SukiTheme.GetInstance().OnBaseThemeChanged -= OnBaseThemeChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
||||||
{
|
{
|
||||||
var registryOptions = new RegistryOptions(
|
var registryOptions = new RegistryOptions(
|
||||||
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
||||||
|
|
||||||
var responseTmi = _responseEditor.InstallTextMate(registryOptions);
|
|
||||||
responseTmi.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions
|
|
||||||
.GetLanguageByExtension(".json").Id));
|
|
||||||
var requestTmi = _requestEditor.InstallTextMate(registryOptions);
|
|
||||||
requestTmi.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions
|
|
||||||
.GetLanguageByExtension(".json").Id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,39 +2,67 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:theme="clr-namespace:SukiUI.Theme;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
|
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
|
||||||
|
xmlns:controls="using:Needlework.Net.Desktop.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Desktop.Views.EndpointView"
|
x:Class="Needlework.Net.Desktop.Views.EndpointView"
|
||||||
x:DataType="vm:EndpointViewModel">
|
x:DataType="vm:EndpointViewModel">
|
||||||
<Grid RowDefinitions="auto,*" ColumnDefinitions="3*,2,4*,2,4*">
|
<UserControl.Styles>
|
||||||
|
<Style Selector="DataGrid">
|
||||||
|
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ControlElevationBorderBrush}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="DataGridColumnHeader TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="DataGridRow DataGridCell">
|
||||||
|
<Setter Property="FontSize" Value="12"></Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="DataGridRow">
|
||||||
|
<Setter Property="Margin" Value="0 0 0 4"></Setter>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="TabItem > TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="FontWeight" Value="DemiBold"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ListBox ListBoxItem">
|
||||||
|
<Setter Property="MinHeight" Value="30"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
<Grid RowDefinitions="auto,*" ColumnDefinitions="3*,auto,4*,auto,4*">
|
||||||
<Grid Grid.Row="0"
|
<Grid Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.RowSpan="2"
|
RowDefinitions="*"
|
||||||
RowDefinitions="auto,*"
|
ColumnDefinitions="auto,*">
|
||||||
|
<TextBox Text="{Binding Search}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Grid.ColumnSpan="2"/>
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
RowDefinitions="*"
|
||||||
ColumnDefinitions="*">
|
ColumnDefinitions="*">
|
||||||
<ListBox ItemsSource="{Binding PathOperations}"
|
<ListBox ItemsSource="{Binding FilteredPathOperations}"
|
||||||
SelectedItem="{Binding SelectedPathOperation}"
|
SelectedItem="{Binding SelectedPathOperation}"
|
||||||
ScrollViewer.HorizontalScrollBarVisibility="Visible"
|
ScrollViewer.HorizontalScrollBarVisibility="Visible"
|
||||||
Margin="0 0 0 0"
|
Margin="0 0 0 0"
|
||||||
Grid.Row="0"
|
Grid.Row="1"
|
||||||
Grid.RowSpan="2"
|
|
||||||
Grid.Column="0">
|
Grid.Column="0">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid
|
<Grid
|
||||||
RowDefinitions="*"
|
RowDefinitions="*"
|
||||||
ColumnDefinitions="auto,*">
|
ColumnDefinitions="auto,*">
|
||||||
<Button
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Classes="Flat"
|
TextAlignment="Center"
|
||||||
Margin="0 0 8 0"
|
Margin="0 0 8 0"
|
||||||
Content="{Binding Method}"
|
Text="{Binding Method}"
|
||||||
Background="{Binding Color}"
|
Background="{Binding Color}"
|
||||||
FontSize="8"
|
FontSize="8"
|
||||||
Width="45"
|
Width="50"
|
||||||
Padding="10 2 10 2"
|
Padding="10 2 10 2"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"/>
|
Grid.Column="0"/>
|
||||||
@@ -50,6 +78,7 @@
|
|||||||
</ListBox>
|
</ListBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
<GridSplitter Background="Gray"
|
<GridSplitter Background="Gray"
|
||||||
|
Margin="8 0 8 0"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.RowSpan="2"
|
Grid.RowSpan="2"
|
||||||
Grid.Column="1"/>
|
Grid.Column="1"/>
|
||||||
@@ -61,7 +90,8 @@
|
|||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Text="{Binding SelectedPathOperation.Method}"
|
Text="{Binding SelectedPathOperation.Method}"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
IsReadOnly="True"/>
|
IsReadOnly="True"
|
||||||
|
Margin="0 0 8 0"/>
|
||||||
<TextBox Grid.Row="0"
|
<TextBox Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
@@ -84,58 +114,56 @@
|
|||||||
<TabItem Header="Params">
|
<TabItem Header="Params">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel IsVisible="{Binding SelectedPathOperation, Converter={StaticResource NullBoolConverter}}">
|
<StackPanel IsVisible="{Binding SelectedPathOperation, Converter={StaticResource NullBoolConverter}}">
|
||||||
<suki:GroupBox Header="Path Parameters"
|
<controls:Card
|
||||||
Margin="0 4"
|
Margin="0 4"
|
||||||
IsVisible="{Binding SelectedPathOperation.Operation.PathParameters, Converter={StaticResource EnumerableBoolConverter}}">
|
IsVisible="{Binding SelectedPathOperation.Operation.PathParameters, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
<DataGrid
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="14"
|
||||||
|
FontWeight="DemiBold">Path Parameters</TextBlock>
|
||||||
|
<DataGrid
|
||||||
ItemsSource="{Binding SelectedPathOperation.Operation.PathParameters}"
|
ItemsSource="{Binding SelectedPathOperation.Operation.PathParameters}"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
GridLinesVisibility="Horizontal">
|
GridLinesVisibility="All">
|
||||||
<DataGrid.Styles>
|
<DataGrid.Columns>
|
||||||
<Style Selector="DataGridRow DataGridCell">
|
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
|
||||||
<Setter Property="FontSize" Value="12"></Setter>
|
<DataGridCheckBoxColumn Header="Required" Binding="{Binding IsRequired}"/>
|
||||||
</Style>
|
<DataGridTemplateColumn Header="Value">
|
||||||
</DataGrid.Styles>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataGrid.Columns>
|
<DataTemplate DataType="vm:ParameterViewModel">
|
||||||
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
|
<TextBox Text="{Binding Value}"/>
|
||||||
<DataGridCheckBoxColumn Header="Required" Binding="{Binding IsRequired}"/>
|
</DataTemplate>
|
||||||
<DataGridTemplateColumn Header="Value">
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn>
|
||||||
<DataTemplate DataType="vm:ParameterViewModel">
|
<DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
|
||||||
<TextBox Text="{Binding Value}"/>
|
</DataGrid.Columns>
|
||||||
</DataTemplate>
|
</DataGrid>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</StackPanel>
|
||||||
</DataGridTemplateColumn>
|
</controls:Card>
|
||||||
<DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
|
<controls:Card
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</suki:GroupBox>
|
|
||||||
<suki:GroupBox Header="Query Parameters"
|
|
||||||
Margin="0 4"
|
Margin="0 4"
|
||||||
IsVisible="{Binding SelectedPathOperation.Operation.QueryParameters, Converter={StaticResource EnumerableBoolConverter}}">
|
IsVisible="{Binding SelectedPathOperation.Operation.QueryParameters, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
<DataGrid
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="14"
|
||||||
|
FontWeight="DemiBold">Query Parameters</TextBlock>
|
||||||
|
<DataGrid
|
||||||
ItemsSource="{Binding SelectedPathOperation.Operation.QueryParameters}"
|
ItemsSource="{Binding SelectedPathOperation.Operation.QueryParameters}"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
GridLinesVisibility="Horizontal">
|
GridLinesVisibility="Horizontal">
|
||||||
<DataGrid.Styles>
|
<DataGrid.Columns>
|
||||||
<Style Selector="DataGridRow DataGridCell">
|
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
|
||||||
<Setter Property="FontSize" Value="12"></Setter>
|
<DataGridCheckBoxColumn Header="Required" Binding="{Binding IsRequired}"/>
|
||||||
</Style>
|
<DataGridTemplateColumn Header="Value">
|
||||||
</DataGrid.Styles>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
<DataGrid.Columns>
|
<DataTemplate DataType="vm:ParameterViewModel">
|
||||||
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
|
<TextBox Text="{Binding Value}"/>
|
||||||
<DataGridCheckBoxColumn Header="Required" Binding="{Binding IsRequired}"/>
|
</DataTemplate>
|
||||||
<DataGridTemplateColumn Header="Value">
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn>
|
||||||
<DataTemplate DataType="vm:ParameterViewModel">
|
<DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
|
||||||
<TextBox Text="{Binding Value}"/>
|
</DataGrid.Columns>
|
||||||
</DataTemplate>
|
</DataGrid>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</StackPanel>
|
||||||
</DataGridTemplateColumn>
|
</controls:Card>
|
||||||
<DataGridTextColumn Header="Type" Binding="{Binding Type}"/>
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</suki:GroupBox>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
@@ -159,6 +187,7 @@
|
|||||||
<TextBox FontSize="12"
|
<TextBox FontSize="12"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
Margin="0 0 0 8"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
Text="{Binding SelectedPathOperation.ResponseUsername}" />
|
Text="{Binding SelectedPathOperation.ResponseUsername}" />
|
||||||
<TextBlock FontSize="12"
|
<TextBlock FontSize="12"
|
||||||
@@ -170,6 +199,7 @@
|
|||||||
<TextBox FontSize="12"
|
<TextBox FontSize="12"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
Margin="0 0 0 8"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
Text="{Binding SelectedPathOperation.ResponsePassword}"/>
|
Text="{Binding SelectedPathOperation.ResponsePassword}"/>
|
||||||
<TextBlock FontSize="12"
|
<TextBlock FontSize="12"
|
||||||
@@ -188,104 +218,85 @@
|
|||||||
<TabItem Header="Schemas">
|
<TabItem Header="Schemas">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<suki:GlassCard Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestBodyType, Converter={StaticResource NullBoolConverter}}">
|
<controls:Card Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestBodyType, Converter={StaticResource NullBoolConverter}}">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<Run Text="Request body: " FontWeight="DemiBold" FontSize="12"/>
|
<Run Text="Request body: " FontWeight="DemiBold" FontSize="12"/>
|
||||||
<Run Text="{Binding SelectedPathOperation.Operation.RequestBodyType}" FontSize="12"/>
|
<Run Text="{Binding SelectedPathOperation.Operation.RequestBodyType}" FontSize="12"/>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</suki:GlassCard>
|
</controls:Card>
|
||||||
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestClasses, Converter={StaticResource EnumerableBoolConverter}}">
|
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.RequestClasses, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
<ItemsRepeater ItemsSource="{Binding SelectedPathOperation.Operation.RequestClasses}">
|
<StackPanel>
|
||||||
<ItemsRepeater.ItemTemplate>
|
<TextBlock FontSize="14" FontWeight="DemiBold" Margin="0 0 0 4">Request Classes</TextBlock>
|
||||||
<DataTemplate>
|
<ItemsRepeater ItemsSource="{Binding SelectedPathOperation.Operation.RequestClasses}">
|
||||||
<StackPanel Margin="0 4 0 8">
|
<ItemsRepeater.ItemTemplate>
|
||||||
<suki:GlassCard IsVisible="{Binding PropertyFields, Converter={StaticResource EnumerableBoolConverter}}">
|
<DataTemplate>
|
||||||
<suki:GroupBox Header="{Binding Id}">
|
<StackPanel Margin="0 4 0 8">
|
||||||
|
<TextBlock FontSize="12" FontWeight="DemiBold" Text="{Binding Id}"/>
|
||||||
|
<controls:Card IsVisible="{Binding PropertyFields, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
<DataGrid
|
<DataGrid
|
||||||
ItemsSource="{Binding PropertyFields}"
|
ItemsSource="{Binding PropertyFields}"
|
||||||
|
AutoGenerateColumns="True"
|
||||||
|
IsReadOnly="True"
|
||||||
|
GridLinesVisibility="Horizontal">
|
||||||
|
</DataGrid>
|
||||||
|
</controls:Card>
|
||||||
|
<controls:Card Margin="0 0 0 8" IsVisible="{Binding PropertyEnums, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
|
<DataGrid
|
||||||
|
ItemsSource="{Binding PropertyEnums}"
|
||||||
AutoGenerateColumns="True"
|
AutoGenerateColumns="True"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
GridLinesVisibility="Horizontal">
|
GridLinesVisibility="Horizontal">
|
||||||
<DataGrid.Styles>
|
|
||||||
<Style Selector="DataGridRow DataGridCell">
|
|
||||||
<Setter Property="FontSize" Value="12"></Setter>
|
|
||||||
</Style>
|
|
||||||
</DataGrid.Styles>
|
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</suki:GroupBox>
|
</controls:Card>
|
||||||
</suki:GlassCard>
|
</StackPanel>
|
||||||
<suki:GlassCard Margin="0 0 0 8" IsVisible="{Binding PropertyEnums, Converter={StaticResource EnumerableBoolConverter}}">
|
</DataTemplate>
|
||||||
<suki:GroupBox Header="{Binding Id}">
|
</ItemsRepeater.ItemTemplate>
|
||||||
<DataGrid
|
</ItemsRepeater>
|
||||||
ItemsSource="{Binding PropertyEnums}"
|
</StackPanel>
|
||||||
AutoGenerateColumns="True"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal">
|
|
||||||
<DataGrid.Styles>
|
|
||||||
<Style Selector="DataGridRow DataGridCell">
|
|
||||||
<Setter Property="FontSize" Value="12"></Setter>
|
|
||||||
</Style>
|
|
||||||
</DataGrid.Styles>
|
|
||||||
</DataGrid>
|
|
||||||
</suki:GroupBox>
|
|
||||||
</suki:GlassCard>
|
|
||||||
</StackPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsRepeater.ItemTemplate>
|
|
||||||
</ItemsRepeater>
|
|
||||||
</Border>
|
</Border>
|
||||||
<suki:GlassCard Margin="0 4">
|
<controls:Card Margin="0 4">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<Run Text="Return value: " FontWeight="DemiBold" FontSize="12"/>
|
<Run Text="Return value: " FontWeight="DemiBold" FontSize="12"/>
|
||||||
<Run Text="{Binding SelectedPathOperation.Operation.ReturnType}" FontSize="12"/>
|
<Run Text="{Binding SelectedPathOperation.Operation.ReturnType}" FontSize="12"/>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</suki:GlassCard>
|
</controls:Card>
|
||||||
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.ResponseClasses, Converter={StaticResource EnumerableBoolConverter}}">
|
<Border Margin="0 4" IsVisible="{Binding SelectedPathOperation.Operation.ResponseClasses, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
<ItemsRepeater ItemsSource="{Binding SelectedPathOperation.Operation.ResponseClasses}">
|
<StackPanel>
|
||||||
<ItemsRepeater.ItemTemplate>
|
<TextBlock FontSize="14" FontWeight="DemiBold">Response Classes</TextBlock>
|
||||||
<DataTemplate>
|
<ItemsRepeater ItemsSource="{Binding SelectedPathOperation.Operation.ResponseClasses}">
|
||||||
<StackPanel Margin="0 4 0 8">
|
<ItemsRepeater.ItemTemplate>
|
||||||
<suki:GlassCard IsVisible="{Binding PropertyFields, Converter={StaticResource EnumerableBoolConverter}}">
|
<DataTemplate>
|
||||||
<suki:GroupBox Header="{Binding Id}">
|
<StackPanel Margin="0 4 0 8">
|
||||||
|
<TextBlock FontSize="12" FontWeight="DemiBold" Text="{Binding Id}" Margin="0 0 0 4"/>
|
||||||
|
<controls:Card IsVisible="{Binding PropertyFields, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
<DataGrid
|
<DataGrid
|
||||||
ItemsSource="{Binding PropertyFields}"
|
ItemsSource="{Binding PropertyFields}"
|
||||||
AutoGenerateColumns="True"
|
|
||||||
IsReadOnly="True"
|
|
||||||
GridLinesVisibility="Horizontal">
|
|
||||||
<DataGrid.Styles>
|
|
||||||
<Style Selector="DataGridRow DataGridCell">
|
|
||||||
<Setter Property="FontSize" Value="12"></Setter>
|
|
||||||
</Style>
|
|
||||||
</DataGrid.Styles>
|
|
||||||
</DataGrid>
|
|
||||||
</suki:GroupBox>
|
|
||||||
</suki:GlassCard>
|
|
||||||
<suki:GlassCard Margin="0 0 0 8" IsVisible="{Binding PropertyEnums, Converter={StaticResource EnumerableBoolConverter}}">
|
|
||||||
<suki:GroupBox Header="{Binding Id}">
|
|
||||||
<DataGrid
|
|
||||||
ItemsSource="{Binding PropertyEnums}"
|
|
||||||
AutoGenerateColumns="True"
|
AutoGenerateColumns="True"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
GridLinesVisibility="Horizontal">
|
GridLinesVisibility="Horizontal">
|
||||||
<DataGrid.Styles>
|
|
||||||
<Style Selector="DataGridRow DataGridCell">
|
|
||||||
<Setter Property="FontSize" Value="12"></Setter>
|
|
||||||
</Style>
|
|
||||||
</DataGrid.Styles>
|
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
</suki:GroupBox>
|
</controls:Card>
|
||||||
</suki:GlassCard>
|
<controls:Card Margin="0 0 0 8" IsVisible="{Binding PropertyEnums, Converter={StaticResource EnumerableBoolConverter}}">
|
||||||
</StackPanel>
|
<DataGrid
|
||||||
</DataTemplate>
|
ItemsSource="{Binding PropertyEnums}"
|
||||||
</ItemsRepeater.ItemTemplate>
|
AutoGenerateColumns="True"
|
||||||
</ItemsRepeater>
|
IsReadOnly="True"
|
||||||
|
GridLinesVisibility="Horizontal">
|
||||||
|
</DataGrid>
|
||||||
|
</controls:Card>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsRepeater.ItemTemplate>
|
||||||
|
</ItemsRepeater>
|
||||||
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
<GridSplitter Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Background="Gray"/>
|
<GridSplitter Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Background="Gray"
|
||||||
|
Margin="8 0 8 0"/>
|
||||||
<StackPanel Grid.Row="0" Grid.Column="4" Orientation="Horizontal">
|
<StackPanel Grid.Row="0" Grid.Column="4" Orientation="Horizontal">
|
||||||
<Button HorizontalAlignment="Left"
|
<Button HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -304,6 +315,7 @@
|
|||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Visible"
|
VerticalScrollBarVisibility="Visible"
|
||||||
ShowLineNumbers="True"
|
ShowLineNumbers="True"
|
||||||
|
IsReadOnly="True"
|
||||||
Text=""
|
Text=""
|
||||||
FontSize="12"/>
|
FontSize="12"/>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using AvaloniaEdit;
|
using AvaloniaEdit;
|
||||||
using AvaloniaEdit.TextMate;
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Needlework.Net.Desktop.Extensions;
|
using Needlework.Net.Desktop.Extensions;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using Needlework.Net.Desktop.ViewModels;
|
using Needlework.Net.Desktop.ViewModels;
|
||||||
using SukiUI;
|
|
||||||
using TextMateSharp.Grammars;
|
using TextMateSharp.Grammars;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.Views;
|
namespace Needlework.Net.Desktop.Views;
|
||||||
@@ -37,7 +35,6 @@ public partial class EndpointView : UserControl, IRecipient<EditorUpdateMessage>
|
|||||||
WeakReferenceMessenger.Default.Register<ContentRequestMessage, string>(this, "EndpointRequestEditor");
|
WeakReferenceMessenger.Default.Register<ContentRequestMessage, string>(this, "EndpointRequestEditor");
|
||||||
|
|
||||||
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
||||||
SukiTheme.GetInstance().OnBaseThemeChanged += OnBaseThemeChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
@@ -45,20 +42,12 @@ public partial class EndpointView : UserControl, IRecipient<EditorUpdateMessage>
|
|||||||
base.OnDetachedFromVisualTree(e);
|
base.OnDetachedFromVisualTree(e);
|
||||||
|
|
||||||
WeakReferenceMessenger.Default.UnregisterAll(this);
|
WeakReferenceMessenger.Default.UnregisterAll(this);
|
||||||
SukiTheme.GetInstance().OnBaseThemeChanged -= OnBaseThemeChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
||||||
{
|
{
|
||||||
var registryOptions = new RegistryOptions(
|
var registryOptions = new RegistryOptions(
|
||||||
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
||||||
|
|
||||||
var requestTmi = _requestEditor.InstallTextMate(registryOptions);
|
|
||||||
requestTmi.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions
|
|
||||||
.GetLanguageByExtension(".json").Id));
|
|
||||||
var responseTmi = _requestEditor.InstallTextMate(registryOptions);
|
|
||||||
responseTmi.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions
|
|
||||||
.GetLanguageByExtension(".json").Id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Receive(EditorUpdateMessage message)
|
public void Receive(EditorUpdateMessage message)
|
||||||
|
|||||||
@@ -2,13 +2,31 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
|
||||||
xmlns:theme="clr-namespace:SukiUI.Theme;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
|
xmlns:avalonEdit="https://github.com/avaloniaui/avaloniaedit"
|
||||||
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Desktop.Views.EndpointsContainerView"
|
x:Class="Needlework.Net.Desktop.Views.EndpointsContainerView"
|
||||||
x:DataType="vm:EndpointsContainerViewModel">
|
x:DataType="vm:EndpointsContainerViewModel">
|
||||||
<suki:SukiStackPage Content="{Binding ActiveViewModel}"
|
<Grid RowDefinitions="auto,*"
|
||||||
Margin="-24 -4 0 0"/>
|
ColumnDefinitions="*"
|
||||||
|
Margin="16">
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0 0 0 8">
|
||||||
|
<Button Command="{Binding GoBackCommand}"
|
||||||
|
Theme="{StaticResource TransparentButton}"
|
||||||
|
Margin="0 0 8 0">
|
||||||
|
<i:Icon Value="fa-arrow-left"
|
||||||
|
FontSize="20"/>
|
||||||
|
</Button>
|
||||||
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
|
||||||
|
Text="{Binding Title}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TransitioningContentControl
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Content="{Binding ActiveViewModel}"/>
|
||||||
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -2,24 +2,31 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
|
||||||
xmlns:theme="clr-namespace:SukiUI.Theme;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
|
xmlns:controls="using:Needlework.Net.Desktop.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
Name="EndpointsControl"
|
||||||
x:Class="Needlework.Net.Desktop.Views.EndpointsView"
|
x:Class="Needlework.Net.Desktop.Views.EndpointsView"
|
||||||
x:DataType="vm:EndpointsViewModel">
|
x:DataType="vm:EndpointsViewModel">
|
||||||
<suki:BusyArea IsBusy="{Binding IsBusy}" BusyText="Loading...">
|
<controls:BusyArea IsBusy="{Binding IsBusy}"
|
||||||
<Grid Margin="16" RowDefinitions="auto,auto,*" ColumnDefinitions="*">
|
BusyText="Loading...">
|
||||||
<TextBox Watermark="Search" Margin="0 4" Text="{Binding Search}" Grid.Row="1" Grid.Column="0"/>
|
<Grid RowDefinitions="auto,auto,*" ColumnDefinitions="*">
|
||||||
<ScrollViewer Grid.Row="2" Grid.Column="0">
|
<TextBox Watermark="Search" Margin="0 4" Text="{Binding Search}" Grid.Row="1" Grid.Column="0"/>
|
||||||
<ListBox ItemsSource="{Binding Query}" SelectedItem="{Binding SelectedQuery}">
|
<ScrollViewer Grid.Row="2" Grid.Column="0">
|
||||||
<ListBox.ItemTemplate>
|
<ItemsRepeater ItemsSource="{Binding Query}">
|
||||||
<DataTemplate>
|
<ItemsRepeater.ItemTemplate>
|
||||||
<TextBlock Text="{Binding}" Foreground="White" />
|
<DataTemplate>
|
||||||
</DataTemplate>
|
<Button Command="{Binding #EndpointsControl.((vm:EndpointsViewModel)DataContext).OpenEndpointCommand}"
|
||||||
</ListBox.ItemTemplate>
|
VerticalAlignment="Stretch"
|
||||||
</ListBox>
|
HorizontalAlignment="Stretch"
|
||||||
</ScrollViewer>
|
HorizontalContentAlignment="Left"
|
||||||
</Grid>
|
CommandParameter="{Binding}"
|
||||||
</suki:BusyArea>
|
Content="{Binding}"
|
||||||
|
Theme="{StaticResource TransparentButton}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsRepeater.ItemTemplate>
|
||||||
|
</ItemsRepeater>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</controls:BusyArea>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -2,74 +2,62 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
|
||||||
xmlns:theme="clr-namespace:SukiUI.Theme;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
|
xmlns:controls="using:Needlework.Net.Desktop.Controls"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Desktop.Views.HomeView"
|
x:Class="Needlework.Net.Desktop.Views.HomeView"
|
||||||
x:DataType="vm:HomeViewModel">
|
x:DataType="vm:HomeViewModel">
|
||||||
<!-- TOP LEVEL -->
|
<!-- TOP LEVEL -->
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<WrapPanel Margin="8"
|
<WrapPanel Margin="8"
|
||||||
theme:WrapPanelExtensions.AnimatedScroll="true"
|
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<!-- WELCOME -->
|
<!-- WELCOME -->
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<suki:GlassCard Margin="8">
|
<Border Margin="12">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Classes="h3">Welcome to Needlework.Net</TextBlock>
|
<TextBlock Theme="{StaticResource TitleTextBlockStyle}">
|
||||||
|
Welcome to Needlework.Net
|
||||||
|
</TextBlock>
|
||||||
<TextBlock>Get started with LCU development by clicking on the endpoints tab in the left panel.</TextBlock>
|
<TextBlock>Get started with LCU development by clicking on the endpoints tab in the left panel.</TextBlock>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</suki:GlassCard>
|
</Border>
|
||||||
<suki:GlassCard Margin="8" Classes="Accent">
|
<controls:Card Margin="12">
|
||||||
<TextBlock TextWrapping="Wrap">THE PROGRAM IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGMENT, OR OF FITNESS FOR A PARTICULAR PURPOSE. LICENSOR DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE PROGRAM WILL MEET YOUR REQUIREMENTS OR THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. LICENSOR MAKES NO WARRANTIES RESPECTING ANY HARM THAT MAY BE CAUSED BY MALICIOUS USE OF THIS SOFTWARE. LICENSOR FURTHER EXPRESSLY DISCLAIMS ANY WARRANTY OR REPRESENTATION TO AUTHORIZED USERS OR TO ANY THIRD PARTY.</TextBlock>
|
<TextBlock TextWrapping="Wrap">THE PROGRAM IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGMENT, OR OF FITNESS FOR A PARTICULAR PURPOSE. LICENSOR DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE PROGRAM WILL MEET YOUR REQUIREMENTS OR THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. LICENSOR MAKES NO WARRANTIES RESPECTING ANY HARM THAT MAY BE CAUSED BY MALICIOUS USE OF THIS SOFTWARE. LICENSOR FURTHER EXPRESSLY DISCLAIMS ANY WARRANTY OR REPRESENTATION TO AUTHORIZED USERS OR TO ANY THIRD PARTY.</TextBlock>
|
||||||
</suki:GlassCard>
|
</controls:Card>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- STATUS -->
|
|
||||||
<StackPanel>
|
|
||||||
<suki:GlassCard Margin="8" Width="250">
|
|
||||||
<suki:GroupBox Header="Status">
|
|
||||||
<TextBlock FontSize="24" FontWeight="Bold" Margin="0 4" Foreground="{Binding StatusForeground}" Text="{Binding StatusText}" />
|
|
||||||
</suki:GroupBox>
|
|
||||||
</suki:GlassCard>
|
|
||||||
<suki:GlassCard Margin="8" Width="250">
|
|
||||||
<suki:GroupBox Header="Address">
|
|
||||||
<TextBlock Text="{Binding StatusAddress}"/>
|
|
||||||
</suki:GroupBox>
|
|
||||||
</suki:GlassCard>
|
|
||||||
</StackPanel>
|
|
||||||
<!-- LEGAL -->
|
|
||||||
<suki:GlassCard Margin="8" Width="300">
|
|
||||||
<suki:GroupBox Header="Disclaimer">
|
|
||||||
<TextBlock TextWrapping="Wrap">Needlework.Net isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc.</TextBlock>
|
|
||||||
</suki:GroupBox>
|
|
||||||
</suki:GlassCard>
|
|
||||||
<!-- FOOTER -->
|
<!-- FOOTER -->
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<suki:GlassCard Margin="8" Width="400">
|
<controls:Card Margin="12" Width="300">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock>© 2024 - Blossomi Shymae</TextBlock>
|
<TextBlock
|
||||||
<TextBlock>MIT License</TextBlock>
|
Theme="{StaticResource SubtitleTextBlockStyle}"
|
||||||
</StackPanel>
|
Margin="0 0 0 8">Resources</TextBlock>
|
||||||
</suki:GlassCard>
|
<StackPanel Orientation="Horizontal">
|
||||||
<suki:GlassCard Margin="8" Width="400">
|
|
||||||
<suki:GroupBox Header="Resources">
|
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
|
||||||
<StackPanel.Styles>
|
<StackPanel.Styles>
|
||||||
<Style Selector="Button">
|
<Style Selector="Button">
|
||||||
<Setter Property="Command" Value="{Binding OpenUrlCommand}"/>
|
<Setter Property="Command" Value="{Binding OpenUrlCommand}"/>
|
||||||
</Style>
|
</Style>
|
||||||
</StackPanel.Styles>
|
</StackPanel.Styles>
|
||||||
<Button CommandParameter="https://hextechdocs.dev/tag/lcu/" Margin="0 0 16 0">
|
<Button CommandParameter="https://hextechdocs.dev/tag/lcu/" Margin="0 0 8 0">
|
||||||
Hextech Docs
|
Hextech Docs
|
||||||
</Button>
|
</Button>
|
||||||
<Button CommandParameter="https://hextechdocs.dev/getting-started-with-the-lcu-api/">
|
<Button CommandParameter="https://hextechdocs.dev/getting-started-with-the-lcu-api/">
|
||||||
Getting Started
|
Getting Started
|
||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</suki:GroupBox>
|
</StackPanel>
|
||||||
</suki:GlassCard>
|
</controls:Card>
|
||||||
|
<controls:Card Margin="12" Width="300">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock>© 2024 - Blossomi Shymae</TextBlock>
|
||||||
|
<TextBlock>MIT License</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</controls:Card>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<!-- LEGAL -->
|
||||||
|
<controls:Card Margin="12" Width="300">
|
||||||
|
<TextBlock TextWrapping="Wrap">Needlework.Net isn't endorsed by Riot Games and doesn't reflect the views or opinions of Riot Games or anyone officially involved in producing or managing Riot Games properties. Riot Games, and all associated properties are trademarks or registered trademarks of Riot Games, Inc.</TextBlock>
|
||||||
|
</controls:Card>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<suki:SukiWindow
|
<Window
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
|
||||||
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||||
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
xmlns:i="https://github.com/projektanker/icons.avalonia"
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
@@ -13,65 +14,86 @@
|
|||||||
Title="Needlework.Net"
|
Title="Needlework.Net"
|
||||||
Icon="/Assets/app.ico"
|
Icon="/Assets/app.ico"
|
||||||
Width="1280"
|
Width="1280"
|
||||||
Height="720">
|
Height="720">
|
||||||
<suki:SukiWindow.LogoContent>
|
<Grid RowDefinitions="auto,*">
|
||||||
<Image Source="/Assets/app.png"
|
<Grid ColumnDefinitions="auto,auto,*,auto"
|
||||||
Width="20"
|
Background="Transparent"
|
||||||
Height="20"
|
Height="40"
|
||||||
VerticalAlignment="Center"/>
|
Grid.Row="0">
|
||||||
</suki:SukiWindow.LogoContent>
|
<Image Margin="12 4"
|
||||||
<!-- TOP LEVEL -->
|
IsHitTestVisible="False"
|
||||||
<suki:SukiSideMenu ItemsSource="{Binding Pages}">
|
Source="/Assets/app.png"
|
||||||
<!-- ITEMS -->
|
Width="18"
|
||||||
<suki:SukiSideMenu.ItemTemplate>
|
Height="18"
|
||||||
<DataTemplate>
|
DockPanel.Dock="Left"
|
||||||
<suki:SukiSideMenuItem Header="{Binding DisplayName}">
|
Grid.Column="0"/>
|
||||||
<suki:SukiSideMenuItem.Icon>
|
<TextBlock FontSize="12"
|
||||||
<materialIcons:MaterialIcon Kind="{Binding Icon}" />
|
IsHitTestVisible="False"
|
||||||
</suki:SukiSideMenuItem.Icon>
|
VerticalAlignment="Center"
|
||||||
</suki:SukiSideMenuItem>
|
Grid.Column="1">
|
||||||
</DataTemplate>
|
Needlework.Net
|
||||||
</suki:SukiSideMenu.ItemTemplate>
|
</TextBlock>
|
||||||
<!-- FOOTER -->
|
</Grid>
|
||||||
<suki:SukiSideMenu.FooterContent>
|
<ui:NavigationView AlwaysShowHeader="False"
|
||||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
PaneDisplayMode="Left"
|
||||||
<StackPanel.Styles>
|
IsSettingsVisible="False"
|
||||||
<Style Selector="Button.Basic">
|
Grid.Row="1"
|
||||||
<Setter Property="Command" Value="{Binding OpenUrlCommand}" />
|
MenuItemsSource="{Binding MenuItems}"
|
||||||
</Style>
|
SelectedItem="{Binding SelectedMenuItem}">
|
||||||
<Style Selector="materialIcons|MaterialIcon">
|
<ui:NavigationView.PaneFooter>
|
||||||
<Setter Property="Width" Value="25" />
|
<StackPanel Orientation="Vertical">
|
||||||
<Setter Property="Height" Value="25" />
|
<StackPanel.Styles>
|
||||||
</Style>
|
<Style Selector="materialIcons|MaterialIcon">
|
||||||
<Style Selector="i|Icon">
|
<Setter Property="Width" Value="20" />
|
||||||
<Setter Property="FontSize" Value="25" />
|
<Setter Property="Height" Value="20" />
|
||||||
</Style>
|
</Style>
|
||||||
</StackPanel.Styles>
|
<Style Selector="i|Icon">
|
||||||
<Button Classes="Flat"
|
<Setter Property="FontSize" Value="20" />
|
||||||
Content="{Binding Version}"
|
</Style>
|
||||||
FontSize="12"
|
</StackPanel.Styles>
|
||||||
Margin="0 0 4 0"
|
<Button
|
||||||
Padding="12 4 12 4"
|
Theme="{StaticResource TransparentButton}"
|
||||||
VerticalAlignment="Center"/>
|
VerticalAlignment="Center"
|
||||||
<Button Classes="Basic"
|
Command="{Binding OpenUrlCommand}"
|
||||||
VerticalAlignment="Center"
|
CommandParameter="https://github.com/BlossomiShymae/Needlework.Net"
|
||||||
CommandParameter="https://github.com/BlossomiShymae/Needlework.Net"
|
ToolTip.Tip="Open on GitHub."
|
||||||
ToolTip.Tip="Open on GitHub."
|
Margin="4">
|
||||||
Margin="0 0 4 0">
|
<materialIcons:MaterialIcon Kind="Github" />
|
||||||
<StackPanel Orientation="Horizontal">
|
</Button>
|
||||||
<materialIcons:MaterialIcon Kind="Github" Margin="0 0 4 0" />
|
<Button
|
||||||
<TextBlock FontSize="12"
|
Theme="{StaticResource TransparentButton}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="White">Star</TextBlock>
|
Command="{Binding OpenUrlCommand}"
|
||||||
</StackPanel>
|
CommandParameter="https://discord.gg/chEvEX5J4E"
|
||||||
</Button>
|
ToolTip.Tip="Open Discord server."
|
||||||
<Button Classes="Basic"
|
Margin="4">
|
||||||
VerticalAlignment="Center"
|
<i:Icon Value="fa-brand fa-discord" />
|
||||||
CommandParameter="https://discord.gg/chEvEX5J4E"
|
</Button>
|
||||||
ToolTip.Tip="Open Discord server.">
|
</StackPanel>
|
||||||
<i:Icon Value="fa-brand fa-discord" />
|
</ui:NavigationView.PaneFooter>
|
||||||
</Button>
|
<Grid>
|
||||||
</StackPanel>
|
<TransitioningContentControl Content="{Binding CurrentPage}"/>
|
||||||
</suki:SukiSideMenu.FooterContent>
|
<Button Content="{Binding Version}"
|
||||||
</suki:SukiSideMenu>
|
HorizontalAlignment="Right"
|
||||||
</suki:SukiWindow>
|
VerticalAlignment="Bottom"
|
||||||
|
Margin="16"/>
|
||||||
|
<ItemsRepeater ItemsSource="{Binding InfoBarItems}"
|
||||||
|
VerticalAlignment="Bottom">
|
||||||
|
<ItemsRepeater.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border Margin="4">
|
||||||
|
<ui:InfoBar
|
||||||
|
Background="{DynamicResource SolidBackgroundFillColorBaseBrush}"
|
||||||
|
Title="{Binding Title}"
|
||||||
|
IsOpen="{Binding IsOpen}"
|
||||||
|
Severity="{Binding Severity}"
|
||||||
|
Message="{Binding Message}"
|
||||||
|
ActionButton="{Binding ActionButton}"/>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsRepeater.ItemTemplate>
|
||||||
|
</ItemsRepeater>
|
||||||
|
</Grid>
|
||||||
|
</ui:NavigationView>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
using SukiUI.Controls;
|
using FluentAvalonia.UI.Windowing;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.Views;
|
namespace Needlework.Net.Desktop.Views;
|
||||||
|
|
||||||
public partial class MainWindow : SukiWindow
|
public partial class MainWindow : AppWindow
|
||||||
{
|
{
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
<suki:SukiWindow
|
<Window
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Desktop.Views.OopsiesWindow"
|
x:Class="Needlework.Net.Desktop.Views.OopsiesWindow"
|
||||||
@@ -12,34 +11,59 @@
|
|||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Width="560"
|
Width="560"
|
||||||
Height="200">
|
Height="200">
|
||||||
<Grid RowDefinitions="auto,auto,auto" ColumnDefinitions="auto,auto"
|
<Grid RowDefinitions="auto,*">
|
||||||
|
<Grid ColumnDefinitions="auto,auto,*,auto"
|
||||||
|
Background="Transparent"
|
||||||
|
Height="40"
|
||||||
|
Grid.Row="0">
|
||||||
|
<Image Margin="12 4"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Source="/Assets/app.png"
|
||||||
|
Width="18"
|
||||||
|
Height="18"
|
||||||
|
DockPanel.Dock="Left"
|
||||||
|
Grid.Column="0"/>
|
||||||
|
<TextBlock FontSize="12"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Grid.Column="1">
|
||||||
|
Needlework.Net - Oopsies
|
||||||
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
|
<Grid RowDefinitions="auto,auto,auto"
|
||||||
|
ColumnDefinitions="auto,auto"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
Margin="8"
|
Margin="8"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
HorizontalAlignment="Center">
|
HorizontalAlignment="Center">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"
|
|
||||||
Grid.ColumnSpan="2">
|
|
||||||
This response is too large for Needlework.Net to handle for performance reasons.
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="0"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
Margin="0 0 0 12">
|
|
||||||
It can be viewed in an external editor or viewer.
|
|
||||||
</TextBlock>
|
|
||||||
<Button Command="{Binding OpenDefaultEditorCommand}"
|
|
||||||
Grid.Row="2"
|
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="0 0 8 0">
|
Grid.ColumnSpan="2">
|
||||||
Open
|
This response is too large for Needlework.Net to handle for performance reasons.
|
||||||
</Button>
|
</TextBlock>
|
||||||
<Button Command="{Binding CloseDialogCommand}"
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="0"
|
||||||
Margin="8 0 0 0">
|
Grid.ColumnSpan="2"
|
||||||
Cancel
|
Margin="0 0 0 12">
|
||||||
</Button>
|
It can be viewed in an external editor or viewer.
|
||||||
|
</TextBlock>
|
||||||
|
<Button Command="{Binding OpenDefaultEditorCommand}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0 0 8 0">
|
||||||
|
Open
|
||||||
|
</Button>
|
||||||
|
<Button Command="{Binding CloseDialogCommand}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Grid.Row="2"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="8 0 0 0">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</suki:SukiWindow>
|
</Window>
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
using SukiUI.Controls;
|
using FluentAvalonia.UI.Windowing;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.Views;
|
namespace Needlework.Net.Desktop.Views;
|
||||||
|
|
||||||
public partial class OopsiesWindow : SukiWindow
|
public partial class OopsiesWindow : AppWindow
|
||||||
{
|
{
|
||||||
public OopsiesWindow()
|
public OopsiesWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,67 +3,58 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
|
||||||
xmlns:suki="clr-namespace:SukiUI.Controls;assembly=SukiUI"
|
|
||||||
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
xmlns:vm="using:Needlework.Net.Desktop.ViewModels"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Needlework.Net.Desktop.Views.WebsocketView"
|
x:Class="Needlework.Net.Desktop.Views.WebsocketView"
|
||||||
x:DataType="vm:WebsocketViewModel">
|
x:DataType="vm:WebsocketViewModel">
|
||||||
<Grid RowDefinitions="*,2,*" Margin="16">
|
<Grid RowDefinitions="*,auto,*" Margin="16">
|
||||||
<Border Grid.Row="0"
|
<Border Grid.Row="0"
|
||||||
Padding="0 0 0 8">
|
Padding="0 0 0 8">
|
||||||
<suki:GlassCard>
|
<Grid RowDefinitions="auto,*" ColumnDefinitions="*">
|
||||||
<Grid RowDefinitions="auto,*" ColumnDefinitions="*">
|
<Grid
|
||||||
<Grid
|
Grid.Row="0"
|
||||||
Grid.Row="0"
|
Grid.Column="0"
|
||||||
Grid.Column="0"
|
RowDefinitions="*"
|
||||||
RowDefinitions="*"
|
ColumnDefinitions="auto,*,auto,auto">
|
||||||
ColumnDefinitions="auto,*,auto,auto">
|
<Button Margin="0 0 8 0"
|
||||||
<Button Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Command="{Binding ClearCommand}"
|
Command="{Binding ClearCommand}">Clear</Button>
|
||||||
Margin="0 0 8 0">Clear</Button>
|
<TextBox Grid.Row="0"
|
||||||
<TextBox Grid.Row="0"
|
Grid.Column="1"
|
||||||
Grid.Column="1"
|
Text="{Binding Search}"
|
||||||
Margin="0 0 8 0"
|
MaxLines="1"
|
||||||
Text="{Binding Search, Mode=TwoWay}"/>
|
Margin="0 0 8 0"/>
|
||||||
<StackPanel Orientation="Horizontal"
|
<CheckBox
|
||||||
Grid.Row="0"
|
Margin="0 0 8 0"
|
||||||
Grid.Column="2"
|
Grid.Row="0"
|
||||||
Margin="0 0 8 0">
|
Grid.Column="2"
|
||||||
<ToggleSwitch Margin="0 0 0 8"
|
Content="Attach"
|
||||||
IsChecked="{Binding IsAttach}"/>
|
IsChecked="{Binding IsAttach}"/>
|
||||||
<TextBlock Margin="0 6 0 0"
|
<CheckBox
|
||||||
FontSize="18">Attach</TextBlock>
|
Grid.Row="0"
|
||||||
</StackPanel>
|
Grid.Column="3"
|
||||||
<StackPanel Orientation="Horizontal"
|
Content="Tail"
|
||||||
Grid.Row="0"
|
IsChecked="{Binding IsTail}"/>
|
||||||
Grid.Column="3">
|
|
||||||
<ToggleSwitch Margin="0 0 0 8"
|
|
||||||
IsChecked="{Binding IsTail}"/>
|
|
||||||
<TextBlock Margin="0 6 0 0"
|
|
||||||
FontSize="18">Tail</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
<ListBox Grid.Row="1"
|
|
||||||
Grid.Column="0"
|
|
||||||
Name="EventViewer"
|
|
||||||
ItemsSource="{Binding FilteredEventLog}"
|
|
||||||
SelectedItem="{Binding SelectedEventLog}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</suki:GlassCard>
|
<ListBox Grid.Row="1"
|
||||||
|
Grid.Column="0"
|
||||||
|
Name="EventViewer"
|
||||||
|
Margin="0 8 0 0"
|
||||||
|
ItemsSource="{Binding FilteredEventLog}"
|
||||||
|
SelectedItem="{Binding SelectedEventLog}"/>
|
||||||
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<GridSplitter Grid.Row="1" ResizeDirection="Rows" Background="Gray"/>
|
<GridSplitter Grid.Row="1" ResizeDirection="Rows" Background="Gray"/>
|
||||||
<Border Grid.Row="2"
|
<Border Grid.Row="2"
|
||||||
Padding="0 8 0 0">
|
Padding="0 8 0 0">
|
||||||
<suki:GlassCard>
|
<avaloniaEdit:TextEditor
|
||||||
<avaloniaEdit:TextEditor
|
Name="ResponseEditor"
|
||||||
Name="ResponseEditor"
|
ShowLineNumbers="True"
|
||||||
ShowLineNumbers="True"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
VerticalScrollBarVisibility="Visible"
|
||||||
VerticalScrollBarVisibility="Visible"
|
Text=""
|
||||||
Text=""
|
FontSize="12"/>
|
||||||
FontSize="12"/>
|
|
||||||
</suki:GlassCard>
|
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using AvaloniaEdit;
|
using AvaloniaEdit;
|
||||||
using AvaloniaEdit.TextMate;
|
|
||||||
using CommunityToolkit.Mvvm.Messaging;
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
using Needlework.Net.Desktop.Extensions;
|
using Needlework.Net.Desktop.Extensions;
|
||||||
using Needlework.Net.Desktop.Messages;
|
using Needlework.Net.Desktop.Messages;
|
||||||
using Needlework.Net.Desktop.ViewModels;
|
using Needlework.Net.Desktop.ViewModels;
|
||||||
using SukiUI;
|
using System;
|
||||||
using TextMateSharp.Grammars;
|
using TextMateSharp.Grammars;
|
||||||
|
|
||||||
namespace Needlework.Net.Desktop.Views;
|
namespace Needlework.Net.Desktop.Views;
|
||||||
@@ -16,6 +15,8 @@ namespace Needlework.Net.Desktop.Views;
|
|||||||
public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMessage>
|
public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMessage>
|
||||||
{
|
{
|
||||||
private TextEditor? _responseEditor;
|
private TextEditor? _responseEditor;
|
||||||
|
public WebsocketViewModel? _viewModel;
|
||||||
|
private ListBox? _viewer;
|
||||||
|
|
||||||
public WebsocketView()
|
public WebsocketView()
|
||||||
{
|
{
|
||||||
@@ -31,9 +32,9 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
|
|||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
|
||||||
var vm = (WebsocketViewModel)DataContext!;
|
_viewModel = (WebsocketViewModel)DataContext!;
|
||||||
var viewer = this.FindControl<ListBox>("EventViewer");
|
_viewer = this.FindControl<ListBox>("EventViewer");
|
||||||
viewer!.PropertyChanged += (s, e) => { if (vm.IsTail) viewer.ScrollIntoView(vm.EventLog.Count - 1); };
|
_viewModel.EventLog.CollectionChanged += EventLog_CollectionChanged; ;
|
||||||
|
|
||||||
_responseEditor = this.FindControl<TextEditor>("ResponseEditor");
|
_responseEditor = this.FindControl<TextEditor>("ResponseEditor");
|
||||||
_responseEditor?.ApplyJsonEditorSettings();
|
_responseEditor?.ApplyJsonEditorSettings();
|
||||||
@@ -41,7 +42,26 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
|
|||||||
WeakReferenceMessenger.Default.Register(this, nameof(WebsocketViewModel));
|
WeakReferenceMessenger.Default.Register(this, nameof(WebsocketViewModel));
|
||||||
|
|
||||||
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
OnBaseThemeChanged(Application.Current!.ActualThemeVariant);
|
||||||
SukiTheme.GetInstance().OnBaseThemeChanged += OnBaseThemeChanged;
|
}
|
||||||
|
|
||||||
|
private void EventLog_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
Avalonia.Threading.Dispatcher.UIThread.Post(async () =>
|
||||||
|
{
|
||||||
|
if (_viewModel!.IsTail)
|
||||||
|
{
|
||||||
|
await _viewModel.EventLogLock.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_viewer!.ScrollIntoView(_viewModel.EventLog.Count - 1);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException) { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_viewModel.EventLogLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
private void OnBaseThemeChanged(ThemeVariant currentTheme)
|
||||||
@@ -49,9 +69,5 @@ public partial class WebsocketView : UserControl, IRecipient<ResponseUpdatedMess
|
|||||||
|
|
||||||
var registryOptions = new RegistryOptions(
|
var registryOptions = new RegistryOptions(
|
||||||
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
currentTheme == ThemeVariant.Dark ? ThemeName.DarkPlus : ThemeName.LightPlus);
|
||||||
|
|
||||||
var responseTmi = _responseEditor.InstallTextMate(registryOptions);
|
|
||||||
responseTmi.SetGrammar(registryOptions.GetScopeByLanguageId(registryOptions
|
|
||||||
.GetLanguageByExtension(".json").Id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
app-preview.png
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 98 KiB |