From 9f3ade916df4e2e90e7cafa6ac8802ba3d4db158 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Fri, 23 Jan 2026 14:17:04 +0100 Subject: [PATCH] Applied SOLID --- .../Layout/MainLayout.razor.cs | 22 +--- app/MindWork AI Studio/Program.cs | 1 + .../Tools/Services/GlobalShortcutService.cs | 100 ++++++++++++++++++ 3 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/Services/GlobalShortcutService.cs diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor.cs b/app/MindWork AI Studio/Layout/MainLayout.razor.cs index faae40b2..4516b81b 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor.cs +++ b/app/MindWork AI Studio/Layout/MainLayout.razor.cs @@ -97,6 +97,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan // Set the snackbar for the update service: UpdateService.SetBlazorDependencies(this.Snackbar); + GlobalShortcutService.Initialize(); TemporaryChatService.Initialize(); // Should the navigation bar be open by default? @@ -113,7 +114,6 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan await this.UpdateThemeConfiguration(); this.LoadNavItems(); - await this.RegisterGlobalShortcuts(); await base.OnInitializedAsync(); } @@ -247,26 +247,6 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan #endregion - /// - /// Registers global shortcuts based on the current configuration. - /// - private async Task RegisterGlobalShortcuts() - { - // Only register the voice recording shortcut if the preview feature is enabled: - if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.SettingsManager)) - { - var shortcut = this.SettingsManager.ConfigurationData.App.ShortcutVoiceRecording; - if (!string.IsNullOrWhiteSpace(shortcut)) - { - var success = await this.RustService.UpdateGlobalShortcut("voice_recording_toggle", shortcut); - if (success) - this.Logger.LogInformation("Global voice recording shortcut '{Shortcut}' registered successfully.", shortcut); - else - this.Logger.LogWarning("Failed to register global voice recording shortcut '{Shortcut}'.", shortcut); - } - } - } - private void LoadNavItems() { this.navItems = new List(this.GetNavItems()); diff --git a/app/MindWork AI Studio/Program.cs b/app/MindWork AI Studio/Program.cs index e87dc0ca..c577b2f4 100644 --- a/app/MindWork AI Studio/Program.cs +++ b/app/MindWork AI Studio/Program.cs @@ -133,6 +133,7 @@ internal sealed class Program builder.Services.AddHostedService(); builder.Services.AddHostedService(); builder.Services.AddHostedService(); + builder.Services.AddHostedService(); // ReSharper disable AccessToDisposedClosure builder.Services.AddHostedService(_ => rust); diff --git a/app/MindWork AI Studio/Tools/Services/GlobalShortcutService.cs b/app/MindWork AI Studio/Tools/Services/GlobalShortcutService.cs new file mode 100644 index 00000000..23ddb553 --- /dev/null +++ b/app/MindWork AI Studio/Tools/Services/GlobalShortcutService.cs @@ -0,0 +1,100 @@ +using AIStudio.Settings; +using AIStudio.Settings.DataModel; + +using Microsoft.AspNetCore.Components; + +namespace AIStudio.Tools.Services; + +public sealed class GlobalShortcutService : BackgroundService, IMessageBusReceiver +{ + private static bool IS_INITIALIZED; + + private readonly ILogger logger; + private readonly SettingsManager settingsManager; + private readonly MessageBus messageBus; + private readonly RustService rustService; + + public GlobalShortcutService( + ILogger logger, + SettingsManager settingsManager, + MessageBus messageBus, + RustService rustService) + { + this.logger = logger; + this.settingsManager = settingsManager; + this.messageBus = messageBus; + this.rustService = rustService; + + this.messageBus.RegisterComponent(this); + this.ApplyFilters([], [Event.CONFIGURATION_CHANGED]); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + // Wait until the app is fully initialized: + while (!stoppingToken.IsCancellationRequested && !IS_INITIALIZED) + await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); + + // Register shortcuts on startup: + await this.RegisterAllShortcuts(); + } + + public override async Task StopAsync(CancellationToken cancellationToken) + { + this.messageBus.Unregister(this); + await base.StopAsync(cancellationToken); + } + + #region IMessageBusReceiver + + public async Task ProcessMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) + { + switch (triggeredEvent) + { + case Event.CONFIGURATION_CHANGED: + await this.RegisterAllShortcuts(); + break; + } + } + + public Task ProcessMessageWithResult( + ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data) + => Task.FromResult(default); + + #endregion + + private async Task RegisterAllShortcuts() + { + this.logger.LogInformation("Registering global shortcuts."); + + // + // Voice recording shortcut (preview feature) + // + if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.settingsManager)) + { + var shortcut = this.settingsManager.ConfigurationData.App.ShortcutVoiceRecording; + if (!string.IsNullOrWhiteSpace(shortcut)) + { + var success = await this.rustService.UpdateGlobalShortcut("voice_recording_toggle", shortcut); + if (success) + this.logger.LogInformation("Global shortcut 'voice_recording_toggle' ({Shortcut}) registered.", shortcut); + else + this.logger.LogWarning("Failed to register global shortcut 'voice_recording_toggle' ({Shortcut}).", shortcut); + } + else + { + // Disable shortcut when empty + await this.rustService.UpdateGlobalShortcut("voice_recording_toggle", string.Empty); + } + } + else + { + // Disable the shortcut when the preview feature is disabled: + await this.rustService.UpdateGlobalShortcut("voice_recording_toggle", string.Empty); + } + + this.logger.LogInformation("Global shortcuts registration completed."); + } + + public static void Initialize() => IS_INITIALIZED = true; +}