From 18dc8bf9b88f682f547a1bdd585fdee33dc3cbac Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Sun, 2 Aug 2026 20:11:27 +0200 Subject: [PATCH] Routed all notifications through the message bus instead of the snackbar --- .../Assistants/I18N/AssistantI18N.razor.cs | 12 ++++----- .../LogViewer/AssistantLogViewer.razor.cs | 27 +++---------------- .../AssistantPromptOptimizer.razor.cs | 10 +++---- .../VisualBriefingAssistant.razor.Build.cs | 6 ++--- .../VisualBriefingAssistant.razor.Projects.cs | 8 +++--- .../VisualBriefingAssistant.razor.Versions.cs | 10 +++---- .../VisualBriefingAssistant.razor.cs | 2 +- .../MudCopyClipboardButton.razor.cs | 9 +++---- .../Settings/SettingsPanelProviderBase.cs | 2 +- .../Layout/MainLayout.razor.cs | 8 +++++- .../Tools/DataInfoMessage.cs | 16 +++++++++++ app/MindWork AI Studio/Tools/Event.cs | 5 ++++ app/MindWork AI Studio/Tools/MessageBus.cs | 2 ++ 13 files changed, 62 insertions(+), 55 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/DataInfoMessage.cs diff --git a/app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor.cs b/app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor.cs index 49d34783..6ed34316 100644 --- a/app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor.cs +++ b/app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor.cs @@ -478,13 +478,13 @@ public partial class AssistantI18N : AssistantBaseCore { if (this.selectedLanguagePlugin is null) { - this.Snackbar.Add(T("No language plugin selected."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.Translate, T("No language plugin selected."))); return; } if (this.finalLuaCode.Length == 0) { - this.Snackbar.Add(T("No Lua code generated yet."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.Code, T("No Lua code generated yet."))); return; } @@ -500,7 +500,7 @@ public partial class AssistantI18N : AssistantBaseCore if (!File.Exists(pluginFilePath)) { this.Logger.LogError("Plugin file not found: {PluginFilePath}.", pluginFilePath); - this.Snackbar.Add(T("Plugin file not found."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.FindInPage, T("Plugin file not found."))); return; } @@ -514,7 +514,7 @@ public partial class AssistantI18N : AssistantBaseCore if (markerIndex == -1) { this.Logger.LogError("Could not find 'UI_TEXT_CONTENT = {{}}' marker in plugin file: {PluginFilePath}", pluginFilePath); - this.Snackbar.Add(T("Could not find 'UI_TEXT_CONTENT = {}' marker in plugin file."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.FindInPage, T("Could not find 'UI_TEXT_CONTENT = {}' marker in plugin file."))); return; } @@ -524,12 +524,12 @@ public partial class AssistantI18N : AssistantBaseCore // Write the updated content back to the file: await File.WriteAllTextAsync(pluginFilePath, newContent); - this.Snackbar.Add(T("Successfully updated plugin file."), Severity.Success); + await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Translate, T("Successfully updated plugin file."))); } catch (Exception ex) { this.Logger.LogError(ex, "Error writing to plugin file."); - this.Snackbar.Add(T("Error writing to plugin file."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.Translate, T("Error writing to plugin file."))); } } #endif diff --git a/app/MindWork AI Studio/Assistants/LogViewer/AssistantLogViewer.razor.cs b/app/MindWork AI Studio/Assistants/LogViewer/AssistantLogViewer.razor.cs index c08ec8e3..fc746006 100644 --- a/app/MindWork AI Studio/Assistants/LogViewer/AssistantLogViewer.razor.cs +++ b/app/MindWork AI Studio/Assistants/LogViewer/AssistantLogViewer.razor.cs @@ -35,9 +35,6 @@ public partial class AssistantLogViewer : MSGComponentBase [Inject] private RustService RustService { get; init; } = null!; - [Inject] - private ISnackbar Snackbar { get; init; } = null!; - [Inject] private NavigationManager NavigationManager { get; init; } = null!; @@ -215,11 +212,7 @@ public partial class AssistantLogViewer : MSGComponentBase var path = this.CurrentLogPath; if (string.IsNullOrWhiteSpace(path)) { - this.Snackbar.Add(T("The log file path is not available yet."), Severity.Warning, config => - { - config.Icon = Icons.Material.Filled.Folder; - config.IconSize = Size.Large; - }); + await this.MessageBus.SendWarning(new(Icons.Material.Filled.Folder, T("The log file path is not available yet."))); return; } @@ -231,30 +224,18 @@ public partial class AssistantLogViewer : MSGComponentBase catch (Exception e) { this.Logger.LogWarning(e, "Could not open the log file location in the file manager."); - this.Snackbar.Add(T("Could not open the log file location."), Severity.Error, config => - { - config.Icon = Icons.Material.Filled.Folder; - config.IconSize = Size.Large; - }); + await this.MessageBus.SendError(new(Icons.Material.Filled.Folder, T("Could not open the log file location."))); return; } if (response.Success) { - this.Snackbar.Add(T("Opened the log file location."), Severity.Success, config => - { - config.Icon = Icons.Material.Filled.FolderOpen; - config.IconSize = Size.Large; - }); + await this.MessageBus.SendSuccess(new(Icons.Material.Filled.FolderOpen, T("Opened the log file location."))); return; } var issue = string.IsNullOrWhiteSpace(response.Issue) ? T("Unknown error") : response.Issue; - this.Snackbar.Add(string.Format(T("Could not open the log file location: {0}"), issue), Severity.Error, config => - { - config.Icon = Icons.Material.Filled.Folder; - config.IconSize = Size.Large; - }); + await this.MessageBus.SendError(new(Icons.Material.Filled.Folder, string.Format(T("Could not open the log file location: {0}"), issue))); } private void ClearFilters() diff --git a/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs b/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs index 7a5156b2..ac56f2c5 100644 --- a/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs +++ b/app/MindWork AI Studio/Assistants/PromptOptimizer/AssistantPromptOptimizer.razor.cs @@ -562,7 +562,7 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore 1 || replacedPrevious) - this.Snackbar.Add(T("Replaced the previously selected custom prompt guide file."), Severity.Info); + await this.MessageBus.SendInfo(new(Icons.Material.Filled.SwapHoriz, T("Replaced the previously selected custom prompt guide file."))); await this.LoadCustomPromptGuidelineContentAsync(selected); } @@ -572,7 +572,7 @@ public partial class AssistantPromptOptimizer : AssistantBaseCore diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Versions.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Versions.cs index f1a1013c..c27f847f 100644 --- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Versions.cs +++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Versions.cs @@ -118,14 +118,14 @@ public partial class VisualBriefingAssistant if (PathComparer().Equals(Path.GetFullPath(sourcePath), Path.GetFullPath(response.SaveFilePath))) { - this.Snackbar.Add(T("Choose a different export location so the immutable briefing version is not overwritten."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.SaveAs, T("Choose a different export location so the immutable briefing version is not overwritten."))); return; } var verified = await this.Store.OpenIntegrityCheckedVersionAsync(this.selectedBriefing.BriefingId, this.selectedRevisionId); if (verified is null) { - this.Snackbar.Add(T("The selected briefing version failed its integrity check and cannot be exported."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.GppBad, T("The selected briefing version failed its integrity check and cannot be exported."))); return; } @@ -146,7 +146,7 @@ public partial class VisualBriefingAssistant exportedVersion.DocumentHash, source.Length); - this.Snackbar.Add(T("The visual briefing was exported."), Severity.Success); + await this.MessageBus.SendSuccess(new(Icons.Material.Filled.FileDownload, T("The visual briefing was exported."))); } /// @@ -176,7 +176,7 @@ public partial class VisualBriefingAssistant if (!imported.Success) { - this.Snackbar.Add(imported.Issue, Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.FileUpload, imported.Issue)); return; } @@ -190,7 +190,7 @@ public partial class VisualBriefingAssistant imported.RevisionId, imported.WasDeduplicated); - this.Snackbar.Add(imported.WasDeduplicated ? T("This briefing revision was already imported.") : T("The visual briefing was imported."), Severity.Success); + await this.MessageBus.SendSuccess(new(Icons.Material.Filled.FileUpload, imported.WasDeduplicated ? T("This briefing revision was already imported.") : T("The visual briefing was imported."))); } /// diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs index d626b4c1..eb9607a7 100644 --- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs +++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs @@ -217,7 +217,7 @@ public partial class VisualBriefingAssistant : MSGComponentBase "Could not auto-save visual briefing. BriefingId={BriefingId} ExceptionType={ExceptionType}", this.selectedBriefing.BriefingId, exception.GetType().Name); - this.Snackbar.Add(T("The visual briefing settings could not be saved."), Severity.Error); + await this.MessageBus.SendError(new(Icons.Material.Filled.SaveAs, T("The visual briefing settings could not be saved."))); } } diff --git a/app/MindWork AI Studio/Components/MudCopyClipboardButton.razor.cs b/app/MindWork AI Studio/Components/MudCopyClipboardButton.razor.cs index 644034f3..3b74a5a3 100644 --- a/app/MindWork AI Studio/Components/MudCopyClipboardButton.razor.cs +++ b/app/MindWork AI Studio/Components/MudCopyClipboardButton.razor.cs @@ -77,12 +77,9 @@ public partial class MudCopyClipboardButton : ComponentBase break; default: - this.Snackbar.Add(TB("Cannot copy this content type to clipboard."), Severity.Error, config => - { - config.Icon = Icons.Material.Filled.ContentCopy; - config.IconSize = Size.Large; - config.IconColor = Color.Error; - }); + // This component is no MSGComponentBase, so it uses the shared bus instance the same + // way FileExtensionValidation does: + await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.ContentCopy, TB("Cannot copy this content type to clipboard."))); break; } } diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviderBase.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviderBase.cs index 9503365c..454197f6 100644 --- a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviderBase.cs +++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviderBase.cs @@ -47,7 +47,7 @@ public abstract class SettingsPanelProviderBase : SettingsPanelBase else { // No encryption secret available - inform the user: - this.Snackbar.Add(TB("Cannot export the encrypted API key: No enterprise encryption secret is configured."), Severity.Warning); + await this.MessageBus.SendWarning(new(Icons.Material.Filled.Key, TB("Cannot export the encrypted API key: No enterprise encryption secret is configured."))); } } } diff --git a/app/MindWork AI Studio/Layout/MainLayout.razor.cs b/app/MindWork AI Studio/Layout/MainLayout.razor.cs index bf64bd39..5f798655 100644 --- a/app/MindWork AI Studio/Layout/MainLayout.razor.cs +++ b/app/MindWork AI Studio/Layout/MainLayout.razor.cs @@ -112,7 +112,7 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan this.MessageBus.ApplyFilters(this, [], [ Event.UPDATE_AVAILABLE, Event.CONFIGURATION_CHANGED, Event.COLOR_THEME_CHANGED, Event.SHOW_ERROR, - Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED, + Event.SHOW_WARNING, Event.SHOW_SUCCESS, Event.SHOW_INFO, Event.STARTUP_PLUGIN_SYSTEM, Event.PLUGINS_RELOADED, Event.INSTALL_UPDATE, Event.STARTUP_COMPLETED, Event.AI_JOB_CHANGED, Event.AI_JOB_FINISHED, Event.CHAT_GENERATION_CHANGED, Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED, ]); @@ -266,6 +266,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, ILan break; + case Event.SHOW_INFO: + if (data is DataInfoMessage info) + info.Show(this.Snackbar); + + break; + case Event.STARTUP_PLUGIN_SYSTEM: _ = Task.Run(async () => { diff --git a/app/MindWork AI Studio/Tools/DataInfoMessage.cs b/app/MindWork AI Studio/Tools/DataInfoMessage.cs new file mode 100644 index 00000000..6a5e9e62 --- /dev/null +++ b/app/MindWork AI Studio/Tools/DataInfoMessage.cs @@ -0,0 +1,16 @@ +namespace AIStudio.Tools; + +public readonly record struct DataInfoMessage(string Icon, string Message) +{ + public void Show(ISnackbar snackbar) + { + var icon = this.Icon; + snackbar.Add(this.Message, Severity.Info, config => + { + config.Icon = icon; + config.IconSize = Size.Large; + config.HideTransitionDuration = 600; + config.VisibleStateDuration = 10_000; + }); + } +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Event.cs b/app/MindWork AI Studio/Tools/Event.cs index 04ae8eab..96354087 100644 --- a/app/MindWork AI Studio/Tools/Event.cs +++ b/app/MindWork AI Studio/Tools/Event.cs @@ -73,6 +73,11 @@ public enum Event /// SHOW_SUCCESS, + /// + /// Requests display of an informational notification. + /// + SHOW_INFO, + /// /// Carries an event received from the Tauri runtime. /// diff --git a/app/MindWork AI Studio/Tools/MessageBus.cs b/app/MindWork AI Studio/Tools/MessageBus.cs index d0e3452b..60ddb983 100644 --- a/app/MindWork AI Studio/Tools/MessageBus.cs +++ b/app/MindWork AI Studio/Tools/MessageBus.cs @@ -91,6 +91,8 @@ public sealed class MessageBus public Task SendSuccess(DataSuccessMessage dataSuccessMessage) => this.SendMessage(null, Event.SHOW_SUCCESS, dataSuccessMessage); + public Task SendInfo(DataInfoMessage dataInfoMessage) => this.SendMessage(null, Event.SHOW_INFO, dataInfoMessage); + public void DeferMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data = default) { if (this.deferredMessages.TryGetValue(triggeredEvent, out var queue))