Browse Source

Add toggle for image viewer pixel grid

pull/165/head
Ionite 1 year ago
parent
commit
fb888d5462
No known key found for this signature in database
  1. 461
      StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs
  2. 20
      StabilityMatrix.Avalonia/Views/SettingsPage.axaml
  3. 43
      StabilityMatrix.Core/Models/Settings/Settings.cs

461
StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs

@ -51,7 +51,7 @@ namespace StabilityMatrix.Avalonia.ViewModels;
public partial class SettingsViewModel : PageViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly INotificationService notificationService;
private readonly ISettingsManager settingsManager;
private readonly IPrerequisiteHelper prerequisiteHelper;
@ -60,82 +60,91 @@ public partial class SettingsViewModel : PageViewModelBase
private readonly ICompletionProvider completionProvider;
private readonly ITrackedDownloadService trackedDownloadService;
private readonly IModelIndexService modelIndexService;
public SharedState SharedState { get; }
public override string Title => "Settings";
public override IconSource IconSource => new SymbolIconSource {Symbol = Symbol.Settings, IsFilled = true};
public override IconSource IconSource =>
new SymbolIconSource { Symbol = Symbol.Settings, IsFilled = true };
// ReSharper disable once MemberCanBeMadeStatic.Global
public string AppVersion => $"Version {Compat.AppVersion}" +
(Program.IsDebugBuild ? " (Debug)" : "");
public string AppVersion =>
$"Version {Compat.AppVersion}" + (Program.IsDebugBuild ? " (Debug)" : "");
// Theme section
[ObservableProperty] private string? selectedTheme;
public IReadOnlyList<string> AvailableThemes { get; } = new[]
{
"Light",
"Dark",
"System",
};
[ObservableProperty]
private string? selectedTheme;
[ObservableProperty] private CultureInfo selectedLanguage;
public IReadOnlyList<string> AvailableThemes { get; } = new[] { "Light", "Dark", "System", };
[ObservableProperty]
private CultureInfo selectedLanguage;
// ReSharper disable once MemberCanBeMadeStatic.Global
public IReadOnlyList<CultureInfo> AvailableLanguages => Cultures.SupportedCultures;
public IReadOnlyList<float> AnimationScaleOptions { get; } = new[]
{
0f,
0.25f,
0.5f,
0.75f,
1f,
1.25f,
1.5f,
1.75f,
2f,
};
[ObservableProperty] private float selectedAnimationScale;
public IReadOnlyList<float> AnimationScaleOptions { get; } =
new[] { 0f, 0.25f, 0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f, };
[ObservableProperty]
private float selectedAnimationScale;
// Shared folder options
[ObservableProperty] private bool removeSymlinksOnShutdown;
[ObservableProperty]
private bool removeSymlinksOnShutdown;
// Inference UI section
[ObservableProperty] private bool isPromptCompletionEnabled;
[ObservableProperty]
private bool isPromptCompletionEnabled;
[ObservableProperty]
private IReadOnlyList<string> availableTagCompletionCsvs = Array.Empty<string>();
[ObservableProperty]
private string? selectedTagCompletionCsv;
[ObservableProperty]
private bool isCompletionRemoveUnderscoresEnabled;
[ObservableProperty]
private bool isImageViewerPixelGridEnabled;
// Integrations section
[ObservableProperty] private bool isDiscordRichPresenceEnabled;
[ObservableProperty]
private bool isDiscordRichPresenceEnabled;
// Debug section
[ObservableProperty] private string? debugPaths;
[ObservableProperty] private string? debugCompatInfo;
[ObservableProperty] private string? debugGpuInfo;
[ObservableProperty]
private string? debugPaths;
[ObservableProperty]
private string? debugCompatInfo;
[ObservableProperty]
private string? debugGpuInfo;
// Info section
private const int VersionTapCountThreshold = 7;
[ObservableProperty, NotifyPropertyChangedFor(nameof(VersionFlyoutText))] private int versionTapCount;
[ObservableProperty] private bool isVersionTapTeachingTipOpen;
public string VersionFlyoutText => $"You are {VersionTapCountThreshold - VersionTapCount} clicks away from enabling Debug options.";
[ObservableProperty, NotifyPropertyChangedFor(nameof(VersionFlyoutText))]
private int versionTapCount;
[ObservableProperty]
private bool isVersionTapTeachingTipOpen;
public string VersionFlyoutText =>
$"You are {VersionTapCountThreshold - VersionTapCount} clicks away from enabling Debug options.";
public SettingsViewModel(
INotificationService notificationService,
INotificationService notificationService,
ISettingsManager settingsManager,
IPrerequisiteHelper prerequisiteHelper,
IPyRunner pyRunner,
ServiceManager<ViewModelBase> dialogFactory,
ITrackedDownloadService trackedDownloadService,
SharedState sharedState,
SharedState sharedState,
ICompletionProvider completionProvider,
IModelIndexService modelIndexService)
IModelIndexService modelIndexService
)
{
this.notificationService = notificationService;
this.settingsManager = settingsManager;
@ -147,40 +156,58 @@ public partial class SettingsViewModel : PageViewModelBase
this.modelIndexService = modelIndexService;
SharedState = sharedState;
SelectedTheme = settingsManager.Settings.Theme ?? AvailableThemes[1];
SelectedLanguage = Cultures.GetSupportedCultureOrDefault(settingsManager.Settings.Language);
RemoveSymlinksOnShutdown = settingsManager.Settings.RemoveFolderLinksOnShutdown;
SelectedAnimationScale = settingsManager.Settings.AnimationScale;
settingsManager.RelayPropertyFor(this,
vm => vm.SelectedTheme,
settings => settings.Theme);
settingsManager.RelayPropertyFor(this,
settingsManager.RelayPropertyFor(this, vm => vm.SelectedTheme, settings => settings.Theme);
settingsManager.RelayPropertyFor(
this,
vm => vm.IsDiscordRichPresenceEnabled,
settings => settings.IsDiscordRichPresenceEnabled,
true);
settingsManager.RelayPropertyFor(this,
true
);
settingsManager.RelayPropertyFor(
this,
vm => vm.SelectedAnimationScale,
settings => settings.AnimationScale);
settingsManager.RelayPropertyFor(this,
settings => settings.AnimationScale
);
settingsManager.RelayPropertyFor(
this,
vm => vm.SelectedTagCompletionCsv,
settings => settings.TagCompletionCsv);
settingsManager.RelayPropertyFor(this,
settings => settings.TagCompletionCsv
);
settingsManager.RelayPropertyFor(
this,
vm => vm.IsPromptCompletionEnabled,
settings => settings.IsPromptCompletionEnabled,
true);
settingsManager.RelayPropertyFor(this,
true
);
settingsManager.RelayPropertyFor(
this,
vm => vm.IsCompletionRemoveUnderscoresEnabled,
settings => settings.IsCompletionRemoveUnderscoresEnabled,
true);
DebugThrowAsyncExceptionCommand.WithNotificationErrorHandler(notificationService, LogLevel.Warn);
true
);
settingsManager.RelayPropertyFor(
this,
vm => vm.IsImageViewerPixelGridEnabled,
settings => settings.IsImageViewerPixelGridEnabled,
true
);
DebugThrowAsyncExceptionCommand.WithNotificationErrorHandler(
notificationService,
LogLevel.Warn
);
ImportTagCsvCommand.WithNotificationErrorHandler(notificationService, LogLevel.Warn);
}
@ -195,7 +222,8 @@ public partial class SettingsViewModel : PageViewModelBase
partial void OnSelectedThemeChanged(string? value)
{
// In case design / tests
if (Application.Current is null) return;
if (Application.Current is null)
return;
// Change theme
Application.Current.RequestedThemeVariant = value switch
{
@ -207,16 +235,16 @@ public partial class SettingsViewModel : PageViewModelBase
partial void OnSelectedLanguageChanged(CultureInfo? oldValue, CultureInfo newValue)
{
if (oldValue is null || newValue.Name == Cultures.Current.Name) return;
if (oldValue is null || newValue.Name == Cultures.Current.Name)
return;
// Set locale
if (AvailableLanguages.Contains(newValue))
{
Logger.Info("Changing language from {Old} to {New}",
oldValue, newValue);
Logger.Info("Changing language from {Old} to {New}", oldValue, newValue);
Cultures.TrySetSupportedCulture(newValue);
settingsManager.Transaction(s => s.Language = newValue.Name);
var dialog = new BetterContentDialog
{
Title = Resources.Label_RelaunchRequired,
@ -237,11 +265,14 @@ public partial class SettingsViewModel : PageViewModelBase
}
else
{
Logger.Info("Requested invalid language change from {Old} to {New}",
oldValue, newValue);
Logger.Info(
"Requested invalid language change from {Old} to {New}",
oldValue,
newValue
);
}
}
partial void OnRemoveSymlinksOnShutdownChanged(bool value)
{
settingsManager.Transaction(s => s.RemoveFolderLinksOnShutdown = value);
@ -251,29 +282,30 @@ public partial class SettingsViewModel : PageViewModelBase
{
settingsManager.Transaction(s => s.InstalledModelHashes = new HashSet<string>());
await Task.Run(() => settingsManager.IndexCheckpoints());
notificationService.Show("Checkpoint cache reset", "The checkpoint cache has been reset.",
NotificationType.Success);
notificationService.Show(
"Checkpoint cache reset",
"The checkpoint cache has been reset.",
NotificationType.Success
);
}
#region Package Environment
[RelayCommand]
private async Task OpenEnvVarsDialog()
{
var viewModel = dialogFactory.Get<EnvVarsViewModel>();
// Load current settings
var current = settingsManager.Settings.EnvironmentVariables
?? new Dictionary<string, string>();
var current =
settingsManager.Settings.EnvironmentVariables ?? new Dictionary<string, string>();
viewModel.EnvVars = new ObservableCollection<EnvVarKeyPair>(
current.Select(kvp => new EnvVarKeyPair(kvp.Key, kvp.Value)));
current.Select(kvp => new EnvVarKeyPair(kvp.Key, kvp.Value))
);
var dialog = new BetterContentDialog
{
Content = new EnvVarsDialog
{
DataContext = viewModel
},
Content = new EnvVarsDialog { DataContext = viewModel },
PrimaryButtonText = "Save",
IsPrimaryButtonEnabled = true,
CloseButtonText = "Cancel",
@ -321,21 +353,23 @@ public partial class SettingsViewModel : PageViewModelBase
dialog.PrimaryButtonText = "Ok";
await dialog.ShowAsync();
}
#endregion
#region Inference UI
private void UpdateAvailableTagCompletionCsvs()
{
if (!settingsManager.IsLibraryDirSet) return;
if (!settingsManager.IsLibraryDirSet)
return;
var tagsDir = settingsManager.TagsDirectory;
if (!tagsDir.Exists) return;
if (!tagsDir.Exists)
return;
var csvFiles = tagsDir.Info.EnumerateFiles("*.csv");
AvailableTagCompletionCsvs = csvFiles.Select(f => f.Name).ToImmutableArray();
// Set selected to current if exists
var settingsCsv = settingsManager.Settings.TagCompletionCsv;
if (settingsCsv is not null && AvailableTagCompletionCsvs.Contains(settingsCsv))
@ -343,45 +377,48 @@ public partial class SettingsViewModel : PageViewModelBase
SelectedTagCompletionCsv = settingsCsv;
}
}
[RelayCommand(FlowExceptionsToTaskScheduler = true)]
private async Task ImportTagCsv()
{
var storage = App.StorageProvider;
var files = await storage.OpenFilePickerAsync(new FilePickerOpenOptions
{
FileTypeFilter = new List<FilePickerFileType>
var files = await storage.OpenFilePickerAsync(
new FilePickerOpenOptions
{
new("CSV")
FileTypeFilter = new List<FilePickerFileType>
{
Patterns = new[] {"*.csv"},
new("CSV") { Patterns = new[] { "*.csv" }, }
}
}
});
if (files.Count == 0) return;
);
if (files.Count == 0)
return;
var sourceFile = new FilePath(files[0].TryGetLocalPath()!);
var tagsDir = settingsManager.TagsDirectory;
tagsDir.Create();
// Copy to tags directory
var targetFile = tagsDir.JoinFile(sourceFile.Name);
await sourceFile.CopyToAsync(targetFile);
// Update index
UpdateAvailableTagCompletionCsvs();
// Trigger load
completionProvider.BackgroundLoadFromFile(targetFile, true);
notificationService.Show($"Imported {sourceFile.Name}",
$"The {sourceFile.Name} file has been imported.", NotificationType.Success);
notificationService.Show(
$"Imported {sourceFile.Name}",
$"The {sourceFile.Name} file has been imported.",
NotificationType.Success
);
}
#endregion
#region System
/// <summary>
@ -392,27 +429,29 @@ public partial class SettingsViewModel : PageViewModelBase
{
if (!Compat.IsWindows)
{
notificationService.Show(
"Not supported", "This feature is only supported on Windows.");
notificationService.Show("Not supported", "This feature is only supported on Windows.");
return;
}
await using var _ = new MinimumDelay(200, 300);
var shortcutDir = new DirectoryPath(
Environment.GetFolderPath(Environment.SpecialFolder.StartMenu),
"Programs");
"Programs"
);
var shortcutLink = shortcutDir.JoinFile("Stability Matrix.lnk");
var appPath = Compat.AppCurrentPath;
var iconPath = shortcutDir.JoinFile("Stability Matrix.ico");
await Assets.AppIcon.ExtractTo(iconPath);
WindowsShortcuts.CreateShortcut(
shortcutLink, appPath, iconPath, "Stability Matrix");
notificationService.Show("Added to Start Menu",
"Stability Matrix has been added to the Start Menu.", NotificationType.Success);
WindowsShortcuts.CreateShortcut(shortcutLink, appPath, iconPath, "Stability Matrix");
notificationService.Show(
"Added to Start Menu",
"Stability Matrix has been added to the Start Menu.",
NotificationType.Success
);
}
/// <summary>
@ -424,54 +463,62 @@ public partial class SettingsViewModel : PageViewModelBase
{
if (!Compat.IsWindows)
{
notificationService.Show(
"Not supported", "This feature is only supported on Windows.");
notificationService.Show("Not supported", "This feature is only supported on Windows.");
return;
}
// Confirmation dialog
var dialog = new BetterContentDialog
{
Title = "This will create a shortcut for Stability Matrix in the Start Menu for all users",
Title =
"This will create a shortcut for Stability Matrix in the Start Menu for all users",
Content = "You will be prompted for administrator privileges. Continue?",
PrimaryButtonText = "Yes",
CloseButtonText = "Cancel",
DefaultButton = ContentDialogButton.Primary
};
if (await dialog.ShowAsync() != ContentDialogResult.Primary)
{
return;
}
await using var _ = new MinimumDelay(200, 300);
var shortcutDir = new DirectoryPath(
Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu),
"Programs");
"Programs"
);
var shortcutLink = shortcutDir.JoinFile("Stability Matrix.lnk");
var appPath = Compat.AppCurrentPath;
var iconPath = shortcutDir.JoinFile("Stability Matrix.ico");
// We can't directly write to the targets, so extract to temporary directory first
using var tempDir = new TempDirectoryPath();
await Assets.AppIcon.ExtractTo(tempDir.JoinFile("Stability Matrix.ico"));
WindowsShortcuts.CreateShortcut(
tempDir.JoinFile("Stability Matrix.lnk"), appPath, iconPath,
"Stability Matrix");
tempDir.JoinFile("Stability Matrix.lnk"),
appPath,
iconPath,
"Stability Matrix"
);
// Move to target
try
{
var moveLinkResult = await WindowsElevated.MoveFiles(
(tempDir.JoinFile("Stability Matrix.lnk"), shortcutLink),
(tempDir.JoinFile("Stability Matrix.ico"), iconPath));
(tempDir.JoinFile("Stability Matrix.ico"), iconPath)
);
if (moveLinkResult != 0)
{
notificationService.ShowPersistent("Failed to create shortcut", $"Could not copy shortcut",
NotificationType.Error);
notificationService.ShowPersistent(
"Failed to create shortcut",
$"Could not copy shortcut",
NotificationType.Error
);
}
}
catch (Win32Exception e)
@ -481,9 +528,12 @@ public partial class SettingsViewModel : PageViewModelBase
notificationService.Show("Could not create shortcut", "", NotificationType.Warning);
return;
}
notificationService.Show("Added to Start Menu",
"Stability Matrix has been added to the Start Menu for all users.", NotificationType.Success);
notificationService.Show(
"Added to Start Menu",
"Stability Matrix has been added to the Start Menu for all users.",
NotificationType.Success
);
}
public async Task PickNewDataDirectory()
@ -494,10 +544,7 @@ public partial class SettingsViewModel : PageViewModelBase
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
Content = new SelectDataDirectoryDialog
{
DataContext = viewModel
}
Content = new SelectDataDirectoryDialog { DataContext = viewModel }
};
var result = await dialog.ShowAsync();
@ -513,7 +560,7 @@ public partial class SettingsViewModel : PageViewModelBase
{
settingsManager.SetLibraryPath(viewModel.DataDirectory);
}
// Restart
var restartDialog = new BetterContentDialog
{
@ -524,14 +571,14 @@ public partial class SettingsViewModel : PageViewModelBase
IsSecondaryButtonEnabled = false,
};
await restartDialog.ShowAsync();
Process.Start(Compat.AppCurrentPath);
App.Shutdown();
}
}
#endregion
#region Debug Section
public void LoadDebugInfo()
{
@ -547,12 +594,12 @@ public partial class SettingsViewModel : PageViewModelBase
AppData Directory [SpecialFolder.ApplicationData]
"{appData}"
""";
// 1. Check portable mode
var appDir = Compat.AppCurrentDir;
var expectedPortableFile = Path.Combine(appDir, "Data", ".sm-portable");
var isPortableMode = File.Exists(expectedPortableFile);
DebugCompatInfo = $"""
Platform: {Compat.Platform}
AppData: {Compat.AppData}
@ -565,24 +612,27 @@ public partial class SettingsViewModel : PageViewModelBase
IsLibraryDirSet = {settingsManager.IsLibraryDirSet}
IsPortableMode = {settingsManager.IsPortableMode}
""";
// Get Gpu info
var gpuInfo = "";
foreach (var (i, gpu) in HardwareHelper.IterGpuInfo().Enumerate())
{
gpuInfo += $"[{i+1}] {gpu}\n";
gpuInfo += $"[{i + 1}] {gpu}\n";
}
DebugGpuInfo = gpuInfo;
}
// Debug buttons
[RelayCommand]
private void DebugNotification()
{
notificationService.Show(new Notification(
title: "Test Notification",
message: "Here is some message",
type: NotificationType.Information));
notificationService.Show(
new Notification(
title: "Test Notification",
message: "Here is some message",
type: NotificationType.Information
)
);
}
[RelayCommand]
@ -597,8 +647,7 @@ public partial class SettingsViewModel : PageViewModelBase
};
var result = await dialog.ShowAsync();
notificationService.Show(new Notification("Content dialog closed",
$"Result: {result}"));
notificationService.Show(new Notification("Content dialog closed", $"Result: {result}"));
}
[RelayCommand]
@ -606,7 +655,7 @@ public partial class SettingsViewModel : PageViewModelBase
{
throw new OperationCanceledException("Example Message");
}
[RelayCommand(FlowExceptionsToTaskScheduler = true)]
private async Task DebugThrowAsyncException()
{
@ -619,18 +668,19 @@ public partial class SettingsViewModel : PageViewModelBase
private async Task DebugMakeImageGrid()
{
var provider = App.StorageProvider;
var files = await provider.OpenFilePickerAsync(new FilePickerOpenOptions()
{
AllowMultiple = true
});
if (files.Count == 0) return;
var images = await files.SelectAsync(async f =>
SKImage.FromEncodedData(await f.OpenReadAsync()));
var files = await provider.OpenFilePickerAsync(
new FilePickerOpenOptions() { AllowMultiple = true }
);
if (files.Count == 0)
return;
var images = await files.SelectAsync(
async f => SKImage.FromEncodedData(await f.OpenReadAsync())
);
var grid = ImageProcessor.CreateImageGrid(images.ToImmutableArray());
// Show preview
using var peekPixels = grid.PeekPixels();
@ -639,10 +689,7 @@ public partial class SettingsViewModel : PageViewModelBase
var bitmap = WriteableBitmap.Decode(stream);
var galleryImages = new List<ImageSource>
{
new(bitmap),
};
var galleryImages = new List<ImageSource> { new(bitmap), };
galleryImages.AddRange(files.Select(f => new ImageSource(f.Path.ToString())));
var imageBox = new ImageGalleryCard
@ -667,39 +714,34 @@ public partial class SettingsViewModel : PageViewModelBase
await dialog.ShowAsync();
}
[RelayCommand]
private async Task DebugLoadCompletionCsv()
{
var provider = App.StorageProvider;
var files = await provider.OpenFilePickerAsync(new FilePickerOpenOptions());
if (files.Count == 0) return;
if (files.Count == 0)
return;
await completionProvider.LoadFromFile(files[0].TryGetLocalPath()!, true);
notificationService.Show("Loaded completion file", "");
}
[RelayCommand]
private async Task DebugRefreshModelsIndex()
{
await modelIndexService.RefreshIndex();
}
[RelayCommand]
private async Task DebugTrackedDownload()
{
var textFields = new TextBoxField[]
{
new()
{
Label = "Url",
},
new()
{
Label = "File path"
}
new() { Label = "Url", },
new() { Label = "File path" }
};
var dialog = DialogHelper.CreateTextEntryDialog("Add download", "", textFields);
@ -719,10 +761,11 @@ public partial class SettingsViewModel : PageViewModelBase
public void OnVersionClick()
{
// Ignore if already enabled
if (SharedState.IsDebugMode) return;
if (SharedState.IsDebugMode)
return;
VersionTapCount++;
switch (VersionTapCount)
{
// Reached required threshold
@ -732,7 +775,9 @@ public partial class SettingsViewModel : PageViewModelBase
// Enable debug options
SharedState.IsDebugMode = true;
notificationService.Show(
"Debug options enabled", "Warning: Improper use may corrupt application state or cause loss of data.");
"Debug options enabled",
"Warning: Improper use may corrupt application state or cause loss of data."
);
VersionTapCount = 0;
break;
}
@ -756,8 +801,11 @@ public partial class SettingsViewModel : PageViewModelBase
}
catch (Exception e)
{
notificationService.Show("Failed to read licenses information",
$"{e}", NotificationType.Error);
notificationService.Show(
"Failed to read licenses information",
$"{e}",
NotificationType.Error
);
}
}
@ -765,15 +813,17 @@ public partial class SettingsViewModel : PageViewModelBase
{
// Read licenses.json
using var reader = new StreamReader(Assets.LicensesJson.Open());
var licenses = JsonSerializer
.Deserialize<IReadOnlyList<LicenseInfo>>(reader.ReadToEnd()) ??
throw new InvalidOperationException("Failed to read licenses.json");
var licenses =
JsonSerializer.Deserialize<IReadOnlyList<LicenseInfo>>(reader.ReadToEnd())
?? throw new InvalidOperationException("Failed to read licenses.json");
// Generate markdown
var builder = new StringBuilder();
foreach (var license in licenses)
{
builder.AppendLine($"## [{license.PackageName}]({license.PackageUrl}) by {string.Join(", ", license.Authors)}");
builder.AppendLine(
$"## [{license.PackageName}]({license.PackageUrl}) by {string.Join(", ", license.Authors)}"
);
builder.AppendLine();
builder.AppendLine(license.Description);
builder.AppendLine();
@ -785,5 +835,4 @@ public partial class SettingsViewModel : PageViewModelBase
}
#endregion
}

20
StabilityMatrix.Avalonia/Views/SettingsPage.axaml

@ -82,7 +82,7 @@
</Grid>
<!-- Inference UI -->
<Grid Margin="0,8,0,0" RowDefinitions="auto,*">
<Grid Margin="0,8,0,0" RowDefinitions="auto,*,*">
<TextBlock
FontWeight="Medium"
Text="Inference UI"
@ -137,6 +137,24 @@
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
</ui:SettingsExpander>
<!-- Image Viewer -->
<ui:SettingsExpander Grid.Row="2"
Header="Image Viewer"
IconSource="Image"
Margin="8,0,8,4">
<!-- Pixel grid -->
<ui:SettingsExpanderItem
Content="Show pixel grid at high zoom levels"
IconSource="ViewAll"
IsEnabled="{Binding IsPromptCompletionEnabled}">
<ui:SettingsExpanderItem.Footer>
<CheckBox Margin="8"
IsChecked="{Binding IsImageViewerPixelGridEnabled}"/>
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
</ui:SettingsExpander>
</Grid>
<!-- Environment Options -->

43
StabilityMatrix.Core/Models/Settings/Settings.cs

@ -1,17 +1,17 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace StabilityMatrix.Core.Models.Settings;
[JsonSerializable(typeof(Settings))]
public class Settings
{
public int? Version { get; set; } = 1;
public bool FirstLaunchSetupComplete { get; set; }
public string? Theme { get; set; } = "Dark";
public string? Language { get; set; } = "en-US";
public List<InstalledPackage> InstalledPackages { get; set; } = new();
[JsonPropertyName("ActiveInstalledPackage")]
public Guid? ActiveInstalledPackageId { get; set; }
@ -22,48 +22,53 @@ public class Settings
[JsonIgnore]
public InstalledPackage? ActiveInstalledPackage
{
get => ActiveInstalledPackageId == null ? null :
InstalledPackages.FirstOrDefault(x => x.Id == ActiveInstalledPackageId);
get =>
ActiveInstalledPackageId == null
? null
: InstalledPackages.FirstOrDefault(x => x.Id == ActiveInstalledPackageId);
set => ActiveInstalledPackageId = value?.Id;
}
public bool HasSeenWelcomeNotification { get; set; }
public List<string>? PathExtensions { get; set; }
public string? WebApiHost { get; set; }
public string? WebApiPort { get; set; }
// UI states
public bool ModelBrowserNsfwEnabled { get; set; }
public bool IsNavExpanded { get; set; }
public bool IsImportAsConnected { get; set; }
public SharedFolderType? SharedFolderVisibleCategories { get; set; } =
SharedFolderType.StableDiffusion |
SharedFolderType.Lora |
SharedFolderType.LyCORIS;
SharedFolderType.StableDiffusion | SharedFolderType.Lora | SharedFolderType.LyCORIS;
public WindowSettings? WindowSettings { get; set; }
public ModelSearchOptions? ModelSearchOptions { get; set; }
/// <summary>
/// Whether prompt auto completion is enabled
/// </summary>
public bool IsPromptCompletionEnabled { get; set; }
/// <summary>
/// Relative path to the tag completion CSV file from 'LibraryDir/Tags'
/// </summary>
public string? TagCompletionCsv { get; set; }
/// <summary>
/// Whether to remove underscores from completions
/// </summary>
public bool IsCompletionRemoveUnderscoresEnabled { get; set; }
/// <summary>
/// Whether the Inference Image Viewer shows pixel grids at high zoom levels
/// </summary>
public bool IsImageViewerPixelGridEnabled { get; set; }
public bool RemoveFolderLinksOnShutdown { get; set; }
public bool IsDiscordRichPresenceEnabled { get; set; }
public Dictionary<string, string>? EnvironmentVariables { get; set; }
public HashSet<string>? InstalledModelHashes { get; set; } = new();
@ -74,13 +79,13 @@ public class Settings
{
RemoveInstalledPackageAndUpdateActive(package.Id);
}
public void RemoveInstalledPackageAndUpdateActive(Guid id)
{
InstalledPackages.RemoveAll(x => x.Id == id);
UpdateActiveInstalledPackage();
}
/// <summary>
/// Update ActiveInstalledPackage if not valid
/// uses first package or null if no packages

Loading…
Cancel
Save