JT
9 months ago
8 changed files with 241 additions and 32 deletions
@ -0,0 +1,135 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Immutable; |
||||||
|
using System.Collections.ObjectModel; |
||||||
|
using System.Linq; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using Avalonia.Controls.Notifications; |
||||||
|
using CommunityToolkit.Mvvm.ComponentModel; |
||||||
|
using Microsoft.Extensions.Logging; |
||||||
|
using StabilityMatrix.Avalonia.Models; |
||||||
|
using StabilityMatrix.Avalonia.ViewModels; |
||||||
|
using StabilityMatrix.Core.Attributes; |
||||||
|
using StabilityMatrix.Core.Extensions; |
||||||
|
using StabilityMatrix.Core.Helper; |
||||||
|
using StabilityMatrix.Core.Helper.Factory; |
||||||
|
using StabilityMatrix.Core.Models; |
||||||
|
using StabilityMatrix.Core.Models.FileInterfaces; |
||||||
|
using StabilityMatrix.Core.Models.Packages; |
||||||
|
using StabilityMatrix.Core.Python; |
||||||
|
using StabilityMatrix.Core.Services; |
||||||
|
|
||||||
|
namespace StabilityMatrix.Avalonia.Services; |
||||||
|
|
||||||
|
[Singleton] |
||||||
|
public partial class RunningPackageService( |
||||||
|
ILogger<RunningPackageService> logger, |
||||||
|
IPackageFactory packageFactory, |
||||||
|
INotificationService notificationService, |
||||||
|
ISettingsManager settingsManager, |
||||||
|
IPyRunner pyRunner |
||||||
|
) : ObservableObject |
||||||
|
{ |
||||||
|
[ObservableProperty] |
||||||
|
private ObservableDictionary<Guid, RunningPackageViewModel> runningPackages = []; |
||||||
|
|
||||||
|
public async Task<Guid?> StartPackage(InstalledPackage installedPackage, string? command = null) |
||||||
|
{ |
||||||
|
var activeInstallName = installedPackage.PackageName; |
||||||
|
var basePackage = string.IsNullOrWhiteSpace(activeInstallName) |
||||||
|
? null |
||||||
|
: packageFactory.GetNewBasePackage(installedPackage); |
||||||
|
|
||||||
|
if (basePackage == null) |
||||||
|
{ |
||||||
|
logger.LogWarning( |
||||||
|
"During launch, package name '{PackageName}' did not match a definition", |
||||||
|
activeInstallName |
||||||
|
); |
||||||
|
|
||||||
|
notificationService.Show( |
||||||
|
new Notification( |
||||||
|
"Package name invalid", |
||||||
|
"Install package name did not match a definition. Please reinstall and let us know about this issue.", |
||||||
|
NotificationType.Error |
||||||
|
) |
||||||
|
); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// If this is the first launch (LaunchArgs is null), |
||||||
|
// load and save a launch options dialog vm |
||||||
|
// so that dynamic initial values are saved. |
||||||
|
if (installedPackage.LaunchArgs == null) |
||||||
|
{ |
||||||
|
var definitions = basePackage.LaunchOptions; |
||||||
|
// Create config cards and save them |
||||||
|
var cards = LaunchOptionCard |
||||||
|
.FromDefinitions(definitions, Array.Empty<LaunchOption>()) |
||||||
|
.ToImmutableArray(); |
||||||
|
|
||||||
|
var args = cards.SelectMany(c => c.Options).ToList(); |
||||||
|
|
||||||
|
logger.LogDebug( |
||||||
|
"Setting initial launch args: {Args}", |
||||||
|
string.Join(", ", args.Select(o => o.ToArgString()?.ToRepr())) |
||||||
|
); |
||||||
|
|
||||||
|
settingsManager.SaveLaunchArgs(installedPackage.Id, args); |
||||||
|
} |
||||||
|
|
||||||
|
if (basePackage is not StableSwarm) |
||||||
|
{ |
||||||
|
await pyRunner.Initialize(); |
||||||
|
} |
||||||
|
|
||||||
|
// Get path from package |
||||||
|
var packagePath = new DirectoryPath(settingsManager.LibraryDir, installedPackage.LibraryPath!); |
||||||
|
|
||||||
|
if (basePackage is not StableSwarm) |
||||||
|
{ |
||||||
|
// Unpack sitecustomize.py to venv |
||||||
|
await UnpackSiteCustomize(packagePath.JoinDir("venv")); |
||||||
|
} |
||||||
|
|
||||||
|
// Clear console and start update processing |
||||||
|
var console = new ConsoleViewModel(); |
||||||
|
console.StartUpdates(); |
||||||
|
|
||||||
|
// Update shared folder links (in case library paths changed) |
||||||
|
await basePackage.UpdateModelFolders( |
||||||
|
packagePath, |
||||||
|
installedPackage.PreferredSharedFolderMethod ?? basePackage.RecommendedSharedFolderMethod |
||||||
|
); |
||||||
|
|
||||||
|
// Load user launch args from settings and convert to string |
||||||
|
var userArgs = installedPackage.LaunchArgs ?? []; |
||||||
|
var userArgsString = string.Join(" ", userArgs.Select(opt => opt.ToArgString())); |
||||||
|
|
||||||
|
// Join with extras, if any |
||||||
|
userArgsString = string.Join(" ", userArgsString, basePackage.ExtraLaunchArguments); |
||||||
|
|
||||||
|
// Use input command if provided, otherwise use package launch command |
||||||
|
command ??= basePackage.LaunchCommand; |
||||||
|
|
||||||
|
await basePackage.RunPackage(packagePath, command, userArgsString, o => console.Post(o)); |
||||||
|
var runningPackage = new PackagePair(installedPackage, basePackage); |
||||||
|
|
||||||
|
EventManager.Instance.OnRunningPackageStatusChanged(runningPackage); |
||||||
|
|
||||||
|
var viewModel = new RunningPackageViewModel(runningPackage, console); |
||||||
|
RunningPackages.Add(runningPackage.InstalledPackage.Id, viewModel); |
||||||
|
|
||||||
|
return runningPackage.InstalledPackage.Id; |
||||||
|
} |
||||||
|
|
||||||
|
public RunningPackageViewModel? GetRunningPackageViewModel(Guid id) => |
||||||
|
RunningPackages.TryGetValue(id, out var vm) ? vm : null; |
||||||
|
|
||||||
|
private static async Task UnpackSiteCustomize(DirectoryPath venvPath) |
||||||
|
{ |
||||||
|
var sitePackages = venvPath.JoinDir(PyVenvRunner.RelativeSitePackagesPath); |
||||||
|
var file = sitePackages.JoinFile("sitecustomize.py"); |
||||||
|
file.Directory?.Create(); |
||||||
|
await Assets.PyScriptSiteCustomize.ExtractTo(file, true); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
using StabilityMatrix.Avalonia.ViewModels.Base; |
||||||
|
using StabilityMatrix.Avalonia.Views; |
||||||
|
using StabilityMatrix.Core.Attributes; |
||||||
|
using StabilityMatrix.Core.Models; |
||||||
|
|
||||||
|
namespace StabilityMatrix.Avalonia.ViewModels; |
||||||
|
|
||||||
|
[View(typeof(ConsoleOutputPage))] |
||||||
|
public class RunningPackageViewModel(PackagePair runningPackage, ConsoleViewModel console) : ViewModelBase |
||||||
|
{ |
||||||
|
public PackagePair RunningPackage { get; } = runningPackage; |
||||||
|
public ConsoleViewModel Console { get; } = console; |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
<controls:UserControlBase 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="clr-namespace:StabilityMatrix.Avalonia.Controls" |
||||||
|
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit" |
||||||
|
xmlns:viewModels="clr-namespace:StabilityMatrix.Avalonia.ViewModels" |
||||||
|
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" /> |
||||||
|
</controls:UserControlBase> |
@ -0,0 +1,13 @@ |
|||||||
|
using StabilityMatrix.Avalonia.Controls; |
||||||
|
using StabilityMatrix.Core.Attributes; |
||||||
|
|
||||||
|
namespace StabilityMatrix.Avalonia.Views; |
||||||
|
|
||||||
|
[Transient] |
||||||
|
public partial class ConsoleOutputPage : UserControlBase |
||||||
|
{ |
||||||
|
public ConsoleOutputPage() |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue