using Avalonia.Controls; using Microsoft.Extensions.DependencyInjection; namespace LanMountainDesktop.PluginSdk; public static class PluginServiceCollectionExtensions { public static IServiceCollection AddPluginSettingsSection( this IServiceCollection services, string id, string titleLocalizationKey, Action configure, string? descriptionLocalizationKey = null, string iconKey = "PuzzlePiece", int sortOrder = 0) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configure); var builder = new PluginSettingsSectionBuilder( id, titleLocalizationKey, descriptionLocalizationKey, iconKey, sortOrder); configure(builder); services.AddSingleton(builder.Build()); return services; } /// /// Registers a plugin settings section with a custom AXAML view. /// The host application will display directly /// in the settings window, allowing the plugin to use any Fluent Avalonia controls /// and custom layouts — just like built-in settings pages. /// /// A subclass that defines the settings UI using AXAML. public static IServiceCollection AddPluginSettingsSection( this IServiceCollection services, string id, string titleLocalizationKey, string? descriptionLocalizationKey = null, string iconKey = "PuzzlePiece", int sortOrder = 0) where TView : SettingsPageBase { ArgumentNullException.ThrowIfNull(services); var builder = new PluginSettingsSectionBuilder( id, titleLocalizationKey, descriptionLocalizationKey, iconKey, sortOrder); builder.SetCustomView(); services.AddSingleton(builder.Build()); return services; } public static IServiceCollection AddPluginDesktopComponent( this IServiceCollection services, PluginDesktopComponentOptions options) where TControl : Control { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(options); services.AddSingleton(new PluginDesktopComponentRegistration( (provider, context) => ActivatorUtilities.CreateInstance(provider, context), options)); return services; } public static IServiceCollection AddPluginDesktopComponentEditor( this IServiceCollection services, string componentId, double preferredWidth = 720d, double preferredHeight = 540d, double minScale = 0.85d, double maxScale = 1.45d) where TControl : Control { ArgumentNullException.ThrowIfNull(services); services.AddSingleton(new PluginDesktopComponentEditorRegistration( componentId, (provider, context) => ActivatorUtilities.CreateInstance(provider, context), preferredWidth, preferredHeight, minScale, maxScale)); return services; } public static IServiceCollection AddPluginExport(this IServiceCollection services) where TContract : class where TImplementation : class, TContract { ArgumentNullException.ThrowIfNull(services); EnsureSingletonRegistration(services); if (!services.Any(descriptor => descriptor.ServiceType == typeof(PluginServiceExportRegistration) && descriptor.ImplementationInstance is PluginServiceExportRegistration existing && existing.ContractType == typeof(TContract) && existing.ImplementationType == typeof(TImplementation))) { services.AddSingleton(new PluginServiceExportRegistration(typeof(TContract), typeof(TImplementation))); } return services; } private static void EnsureSingletonRegistration(IServiceCollection services) where TContract : class where TImplementation : class, TContract { var contractDescriptor = services.LastOrDefault(descriptor => descriptor.ServiceType == typeof(TContract)); if (contractDescriptor is null) { services.AddSingleton(); return; } if (contractDescriptor.Lifetime != ServiceLifetime.Singleton) { throw new InvalidOperationException( $"Exported contract '{typeof(TContract).FullName}' must be registered as Singleton."); } } }