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