Browse Source

added stop buttons

pull/629/head
JT 9 months ago
parent
commit
eb78ec5010
  1. 1
      StabilityMatrix.Avalonia/App.axaml
  2. 9
      StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
  3. 3
      StabilityMatrix.Avalonia/Languages/Resources.resx
  4. 17
      StabilityMatrix.Avalonia/Services/RunningPackageService.cs
  5. 403
      StabilityMatrix.Avalonia/Styles/CommandBarButtonStyles.axaml
  6. 32
      StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs
  7. 116
      StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs
  8. 86
      StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml
  9. 43
      StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs
  10. 24
      StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml
  11. 4
      StabilityMatrix.Core/Helper/EventManager.cs

1
StabilityMatrix.Avalonia/App.axaml

@ -54,6 +54,7 @@
<StyleInclude Source="Styles/DockStyles.axaml"/>
<StyleInclude Source="Styles/BorderStyles.axaml"/>
<StyleInclude Source="Styles/TextBoxStyles.axaml"/>
<StyleInclude Source="Styles/CommandBarButtonStyles.axaml"/>
<StyleInclude Source="Controls/AdvancedImageBox.axaml"/>
<StyleInclude Source="Controls/FrameCarousel.axaml"/>
<StyleInclude Source="Controls/CodeCompletion/CompletionWindow.axaml"/>

9
StabilityMatrix.Avalonia/Languages/Resources.Designer.cs generated

@ -2246,6 +2246,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
/// <summary>
/// Looks up a localized string similar to Auto-Scroll to End.
/// </summary>
public static string Label_ToggleAutoScrolling {
get {
return ResourceManager.GetString("Label_ToggleAutoScrolling", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Trigger words:.
/// </summary>

3
StabilityMatrix.Avalonia/Languages/Resources.resx

@ -975,4 +975,7 @@
<data name="Label_Config" xml:space="preserve">
<value>Config</value>
</data>
<data name="Label_ToggleAutoScrolling" xml:space="preserve">
<value>Auto-Scroll to End</value>
</data>
</root>

17
StabilityMatrix.Avalonia/Services/RunningPackageService.cs

@ -116,12 +116,27 @@ public partial class RunningPackageService(
EventManager.Instance.OnRunningPackageStatusChanged(runningPackage);
var viewModel = new RunningPackageViewModel(runningPackage, console);
var viewModel = new RunningPackageViewModel(
settingsManager,
notificationService,
runningPackage,
console
);
RunningPackages.Add(runningPackage.InstalledPackage.Id, viewModel);
return runningPackage;
}
public async Task StopPackage(Guid id)
{
if (RunningPackages.TryGetValue(id, out var vm))
{
var runningPackage = vm.RunningPackage;
await runningPackage.BasePackage.WaitForShutdown();
RunningPackages.Remove(id);
}
}
public RunningPackageViewModel? GetRunningPackageViewModel(Guid id) =>
RunningPackages.TryGetValue(id, out var vm) ? vm : null;

403
StabilityMatrix.Avalonia/Styles/CommandBarButtonStyles.axaml

@ -0,0 +1,403 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
<Design.PreviewWith>
<Border Padding="20">
<ui:CommandBar
Margin="8"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
HorizontalAlignment="Left"
HorizontalContentAlignment="Left"
DefaultLabelPosition="Right">
<ui:CommandBar.PrimaryCommands>
<ui:CommandBarButton Classes="success" Label="Success Button" Margin="8"
HorizontalAlignment="Center" />
<ui:CommandBarButton Classes="accent" Label="FA Accent Button" Margin="8"
HorizontalAlignment="Center" />
<ui:CommandBarButton Classes="systemaccent" Label="System Accent Button" Margin="8"
HorizontalAlignment="Center" />
<ui:CommandBarButton Classes="danger" Label="Danger Button" Margin="8" HorizontalAlignment="Center" />
<ui:CommandBarButton Classes="info" Label="Info Button" Margin="8" HorizontalAlignment="Center" />
<ui:CommandBarButton Classes="transparent-info" Label="Semi-Transparent Info Button" Margin="8"
HorizontalAlignment="Center" />
<ui:CommandBarButton Classes="transparent" Label="Transparent Button" Margin="8"
HorizontalAlignment="Center" />
<ui:CommandBarButton Classes="transparent-full" Label="Transparent Button" Margin="8"
HorizontalAlignment="Center" />
<ui:CommandBarButton Label="Disabled Button" Margin="8" IsEnabled="False"
HorizontalAlignment="Center" />
</ui:CommandBar.PrimaryCommands>
</ui:CommandBar>
</Border>
</Design.PreviewWith>
<!-- Success -->
<Style Selector="ui|CommandBarButton.success">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeGreenColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeGreenColor}" />
</Style>
<Style Selector="^ /template/ TextBlock#TextLabel">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeDarkGreenColor}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkGreenColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="Green" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeDarkDarkGreenColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkDarkGreenColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!-- Danger -->
<Style Selector="ui|CommandBarButton.danger">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeRedColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeRedColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeDarkRedColor}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkRedColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeDarkDarkRedColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkDarkRedColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!-- Info -->
<Style Selector="ui|CommandBarButton.info">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeLightBlueColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeLightBlueColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeBlueColor}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBlueColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeDarkBlueColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkBlueColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!--Accent Button-->
<Style Selector="ui|CommandBarButton.accent">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackground}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrush}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource AccentButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPointerOver}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPressed}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!-- SystemAccent -->
<Style Selector="ui|CommandBarButton.systemaccent">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource SystemAccentColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorDark1}"/>
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColorDark1}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource SystemAccentColorDark2}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColorDark2}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!-- Transparent -->
<Style Selector="ui|CommandBarButton.transparent">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource SystemControlTransparentBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBackgroundDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBackgroundPressed}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!-- Semi-Transparent Info -->
<Style Selector="ui|CommandBarButton.transparent-info">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeLightBlueColorTransparent}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeLightBlueColorTransparent}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeBlueColorTransparent}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBlueColorTransparent}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeDarkBlueColorTransparent}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkBlueColorTransparent}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!-- Transparent red -->
<Style Selector="ui|CommandBarButton.transparent-red">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource SystemControlTransparentBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeCoralRedColor}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeCoralRedColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ThemeDarkCoralRedColor}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkCoralRedColor}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
<!-- Full Transparent -->
<Style Selector="ui|CommandBarButton.transparent-full">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource SystemControlTransparentBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlTransparentBrush}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Style Selector="^:pointerover">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBackgroundDisabled}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
</Style>
<Style Selector="^:pressed">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBackgroundPressed}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
</Style>
<Style Selector="^:disabled">
<Style Selector="^ /template/ Border#AppBarButtonInnerBorder">
<Setter Property="Background" Value="{DynamicResource SystemControlTransparentBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlTransparentBrush}" />
</Style>
<Style Selector="^">
<Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
</Style>
</Style>
</Styles>

32
StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs

@ -124,6 +124,15 @@ public partial class PackageCardViewModel(
}
}
private void InstanceOnPackageRelaunchRequested(object? sender, InstalledPackage e)
{
if (e.Id != Package?.Id)
return;
navigationService.GoBack();
Launch().SafeFireAndForget();
}
public override async Task OnLoadedAsync()
{
if (Design.IsDesignMode && Package?.DisplayName == "Running Comfy")
@ -135,6 +144,8 @@ public partial class PackageCardViewModel(
if (Design.IsDesignMode || !settingsManager.IsLibraryDirSet || Package is not { } currentPackage)
return;
EventManager.Instance.PackageRelaunchRequested += InstanceOnPackageRelaunchRequested;
if (
packageFactory.FindPackageByName(currentPackage.PackageName)
is { } basePackage
@ -163,6 +174,11 @@ public partial class PackageCardViewModel(
}
}
public override void OnUnloaded()
{
EventManager.Instance.PackageRelaunchRequested -= InstanceOnPackageRelaunchRequested;
}
public async Task Launch()
{
if (Package == null)
@ -248,7 +264,13 @@ public partial class PackageCardViewModel(
basePackage.StartupComplete -= RunningPackageOnStartupComplete;
}
if (Package?.Id != null)
{
runningPackageService.RunningPackages.Remove(Package.Id);
}
IsRunning = false;
ShowWebUiButton = false;
})
.SafeFireAndForget();
}
@ -514,6 +536,16 @@ public partial class PackageCardViewModel(
ProcessRunner.OpenUrl(basePackage.GithubUrl);
}
[RelayCommand]
private async Task Stop()
{
if (Package is null)
return;
await runningPackageService.StopPackage(Package.Id);
IsRunning = false;
}
private async Task<bool> HasUpdate()
{
if (Package == null || IsUnknownPackage || Design.IsDesignMode)

116
StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs

@ -1,18 +1,126 @@
using FluentAvalonia.UI.Controls;
using System;
using System.Threading.Tasks;
using Avalonia.Threading;
using AvaloniaEdit;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Services;
using SymbolIconSource = FluentIcons.Avalonia.Fluent.SymbolIconSource;
namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(ConsoleOutputPage))]
public class RunningPackageViewModel(PackagePair runningPackage, ConsoleViewModel console) : PageViewModelBase
public partial class RunningPackageViewModel : PageViewModelBase
{
public PackagePair RunningPackage { get; } = runningPackage;
public ConsoleViewModel Console { get; } = console;
private readonly INotificationService notificationService;
private string webUiUrl = string.Empty;
public PackagePair RunningPackage { get; }
public ConsoleViewModel Console { get; }
public override string Title => RunningPackage.InstalledPackage.PackageName ?? "Running Package";
public override IconSource IconSource => new SymbolIconSource();
[ObservableProperty]
private bool autoScrollToEnd;
[ObservableProperty]
private bool showWebUiButton;
[ObservableProperty]
private bool isRunning = true;
/// <inheritdoc/>
public RunningPackageViewModel(
ISettingsManager settingsManager,
INotificationService notificationService,
PackagePair runningPackage,
ConsoleViewModel console
)
{
this.notificationService = notificationService;
RunningPackage = runningPackage;
Console = console;
Console.Document.LineCountChanged += DocumentOnLineCountChanged;
RunningPackage.BasePackage.StartupComplete += BasePackageOnStartupComplete;
runningPackage.BasePackage.Exited += BasePackageOnExited;
settingsManager.RelayPropertyFor(
this,
vm => vm.AutoScrollToEnd,
settings => settings.AutoScrollLaunchConsoleToEnd,
true
);
}
private void BasePackageOnExited(object? sender, int e)
{
IsRunning = false;
ShowWebUiButton = false;
}
private void BasePackageOnStartupComplete(object? sender, string url)
{
webUiUrl = url.Replace("0.0.0.0", "127.0.0.1");
ShowWebUiButton = !string.IsNullOrWhiteSpace(webUiUrl);
}
private void DocumentOnLineCountChanged(object? sender, EventArgs e)
{
if (AutoScrollToEnd)
{
EventManager.Instance.OnScrollToBottomRequested();
}
}
[RelayCommand]
private void LaunchPackage()
{
EventManager.Instance.OnPackageRelaunchRequested(RunningPackage.InstalledPackage);
}
[RelayCommand]
private async Task Stop()
{
await RunningPackage.BasePackage.WaitForShutdown();
Console.PostLine($"{Environment.NewLine}Stopped process at {DateTimeOffset.Now}");
await Console.StopUpdatesAsync();
IsRunning = false;
}
[RelayCommand]
private void LaunchWebUi()
{
if (string.IsNullOrEmpty(webUiUrl))
return;
notificationService.TryAsync(
Task.Run(() => ProcessRunner.OpenUrl(webUiUrl)),
"Failed to open URL",
$"{webUiUrl}"
);
}
[RelayCommand]
private async Task SendToConsole(string input)
{
Console.PostLine(input);
if (RunningPackage?.BasePackage is BaseGitPackage gitPackage)
{
var venv = gitPackage.VenvRunner;
var process = venv?.Process;
if (process is not null)
{
await process.StandardInput.WriteLineAsync(input);
}
}
}
}

86
StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml

@ -5,18 +5,82 @@
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:viewModels="clr-namespace:StabilityMatrix.Avalonia.ViewModels"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:avalonia="https://github.com/projektanker/icons.avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="viewModels:RunningPackageViewModel"
x:Class="StabilityMatrix.Avalonia.Views.ConsoleOutputPage">
<avaloniaEdit:TextEditor
x:Name="Console"
Margin="8,8,16,10"
DataContext="{Binding Console}"
Document="{Binding Document}"
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"
IsReadOnly="True"
LineNumbersForeground="DarkSlateGray"
ShowLineNumbers="True"
VerticalScrollBarVisibility="Auto"
WordWrap="True" />
<Grid RowDefinitions="Auto, *, Auto">
<ui:CommandBar Grid.Row="0"
Margin="12,0,0,4"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
HorizontalAlignment="Left"
HorizontalContentAlignment="Left"
DefaultLabelPosition="Right">
<ui:CommandBar.PrimaryCommands>
<ui:CommandBarButton
IconSource="Stop"
IsVisible="{Binding IsRunning}"
Classes="danger"
Command="{Binding StopCommand}"
VerticalAlignment="Center"
Label="{x:Static lang:Resources.Action_Stop}" />
<ui:CommandBarButton
IconSource="Play"
IsVisible="{Binding !IsRunning}"
Classes="success"
Command="{Binding LaunchPackageCommand}"
VerticalAlignment="Center"
Label="{x:Static lang:Resources.Action_Launch}" />
<ui:CommandBarSeparator Margin="6,0,4,0"/>
<ui:CommandBarToggleButton
VerticalAlignment="Center"
IsChecked="{Binding AutoScrollToEnd}"
Label="{x:Static lang:Resources.Label_ToggleAutoScrolling}"
ToolTip.Tip="{x:Static lang:Resources.Label_AutoScrollToEnd}">
<ui:CommandBarToggleButton.IconSource>
<controls:FASymbolIconSource Symbol="fa-solid fa-arrow-down-wide-short" />
</ui:CommandBarToggleButton.IconSource>
</ui:CommandBarToggleButton>
<ui:CommandBarButton
Command="{Binding LaunchWebUiCommand}"
IsVisible="{Binding ShowWebUiButton}"
VerticalAlignment="Center"
Label="{x:Static lang:Resources.Action_OpenWebUI}">
<ui:CommandBarButton.IconSource>
<controls:FASymbolIconSource Symbol="fa-solid fa-up-right-from-square"/>
</ui:CommandBarButton.IconSource>
</ui:CommandBarButton>
</ui:CommandBar.PrimaryCommands>
</ui:CommandBar>
<avaloniaEdit:TextEditor
Grid.Row="1"
x:Name="Console"
Margin="8,8,16,10"
DataContext="{Binding Console}"
Document="{Binding Document}"
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"
IsReadOnly="True"
LineNumbersForeground="DarkSlateGray"
ShowLineNumbers="True"
VerticalScrollBarVisibility="Auto"
WordWrap="True" />
<Grid Grid.Row="2" ColumnDefinitions="*, Auto"
Margin="8,4,8,8">
<TextBox Grid.Column="0" Name="ManualInputBox"
Margin="0,0,8,0"/>
<Button Grid.Column="1"
Classes="accent"
Content="{x:Static lang:Resources.Action_Send}"
Command="{Binding SendToConsoleCommand}"
CommandParameter="{Binding #ManualInputBox.Text}"/>
</Grid>
</Grid>
</controls:UserControlBase>

43
StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs

@ -1,13 +1,54 @@
using StabilityMatrix.Avalonia.Controls;
using System;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Threading;
using AvaloniaEdit;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Helpers;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
namespace StabilityMatrix.Avalonia.Views;
[Transient]
public partial class ConsoleOutputPage : UserControlBase
{
private const int LineOffset = 5;
public ConsoleOutputPage()
{
InitializeComponent();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
TextEditorConfigs.Configure(Console, TextEditorPreset.Console);
}
protected override void OnUnloaded(RoutedEventArgs e)
{
base.OnUnloaded(e);
EventManager.Instance.ScrollToBottomRequested -= OnScrollToBottomRequested;
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
EventManager.Instance.ScrollToBottomRequested += OnScrollToBottomRequested;
}
private void OnScrollToBottomRequested(object? sender, EventArgs e)
{
Dispatcher.UIThread.Invoke(() =>
{
var editor = this.FindControl<TextEditor>("Console");
if (editor?.Document == null)
return;
var line = Math.Max(editor.Document.LineCount - LineOffset, 1);
editor.ScrollToLine(line);
});
}
}

24
StabilityMatrix.Avalonia/Views/PackageManagerPage.axaml

@ -193,6 +193,30 @@
VerticalAlignment="Center"
Text="{Binding InstalledVersion}" />
<Border Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0,8"
Background="#AA000000"
IsVisible="{Binding IsRunning}"
CornerRadius="4"
ZIndex="1">
<Button Classes="danger"
FontSize="16"
MinWidth="100"
MinHeight="40"
Command="{Binding StopCommand}"
HorizontalAlignment="Center"
HorizontalContentAlignment="Center">
<StackPanel Orientation="Horizontal">
<icons:Icon Value="fa-solid fa-stop"
FontSize="20"
Margin="0,0,8,0"/>
<TextBlock Text="{x:Static lang:Resources.Action_Stop}"/>
</StackPanel>
</Button>
</Border>
<!-- Normal packages shows image -->
<controls:BetterAdvancedImage
Grid.Row="2"

4
StabilityMatrix.Core/Helper/EventManager.cs

@ -23,6 +23,7 @@ public class EventManager
public event EventHandler<bool>? DevModeSettingChanged;
public event EventHandler<UpdateInfo>? UpdateAvailable;
public event EventHandler<Guid>? PackageLaunchRequested;
public event EventHandler<InstalledPackage>? PackageRelaunchRequested;
public event EventHandler? ScrollToBottomRequested;
public event EventHandler<ProgressItem>? ProgressChanged;
public event EventHandler<RunningPackageStatusChangedEventArgs>? RunningPackageStatusChanged;
@ -100,4 +101,7 @@ public class EventManager
public void OnRecommendedModelsDialogClosed() =>
RecommendedModelsDialogClosed?.Invoke(this, EventArgs.Empty);
public void OnPackageRelaunchRequested(InstalledPackage package) =>
PackageRelaunchRequested?.Invoke(this, package);
}

Loading…
Cancel
Save