From 4f8266f255c1d112493bcbe10085242b02fc9b5f Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 12 Jan 2026 20:43:45 +0100 Subject: [PATCH] Added an option to hide specific assistants (#625) Co-authored-by: Claude --- CLAUDE.md | 37 ++--- .../Components/AssistantBlock.razor | 53 +++---- .../Components/AssistantBlock.razor.cs | 26 ++-- app/MindWork AI Studio/Pages/Assistants.razor | 134 +++++++++++------- .../Plugins/configuration/plugin.lua | 10 ++ .../Settings/ConfigurableAssistant.cs | 30 ++++ .../Settings/DataModel/DataApp.cs | 5 + .../Settings/DataModel/PreviewFeatures.cs | 2 + .../Tools/AssistantVisibilityExtensions.cs | 100 +++++++++++++ .../Tools/PluginSystem/PluginConfiguration.cs | 3 + .../PluginSystem/PluginFactory.Loading.cs | 4 + .../wwwroot/changelog/v26.1.2.md | 1 + 12 files changed, 293 insertions(+), 112 deletions(-) create mode 100644 app/MindWork AI Studio/Settings/ConfigurableAssistant.cs create mode 100644 app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs diff --git a/CLAUDE.md b/CLAUDE.md index 14aba589..689378d2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ MindWork AI Studio is a cross-platform desktop application for interacting with - **Providers:** Multi-provider architecture supporting OpenAI, Anthropic, Google, Mistral, Perplexity, self-hosted models, and others - **Plugin System:** Lua-based plugin system for language packs, configuration, and future assistant plugins -## Building and Running +## Building ### Prerequisites - .NET 9 SDK @@ -22,39 +22,16 @@ MindWork AI Studio is a cross-platform desktop application for interacting with - Tauri prerequisites (platform-specific dependencies) - **Note:** Development on Linux is discouraged due to complex Tauri dependencies that vary by distribution -### One-Time Setup +### Build ```bash cd app/Build dotnet run build ``` This builds the .NET app as a Tauri "sidecar" binary, which is required even for development. -### Development Workflow -Run these commands in separate terminals: - -**Terminal 1 - Start Rust runtime:** -```bash -cd runtime -cargo tauri dev --no-watch -``` - -**Terminal 2 - Start .NET app:** -```bash -cd "app/MindWork AI Studio" -dotnet run -``` - -The app will start in the Tauri window. Hot reload is supported for .NET code changes. - -### Building for Production -```bash -cd app/Build -dotnet run build -``` -Creates a release build for the current platform and architecture. Output is in `runtime/target/release/`. ### Running Tests -Currently no automated test suite exists in the repository. +Currently, no automated test suite exists in the repository. ## Architecture Details @@ -125,6 +102,14 @@ Plugins can configure: - Preview features visibility - Preselected profiles - Chat templates +- etc. + +When adding configuration options, update: +- `app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs`: In method `TryProcessConfiguration` register new options. +- `app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs`: In method `LoadAll` check for leftover configuration. +- The corresponding data class in `app/MindWork AI Studio/Settings/DataModel/` to call `ManagedConfiguration.Register(...)`, when adding config options (in contrast to complex config. objects) +- `app/MindWork AI Studio/Tools/PluginSystem/PluginConfigurationObject.cs` for parsing logic of complex configuration objects. +- `app/MindWork AI Studio/Plugins/configuration/plugin.lua` to document the new configuration option. ## RAG (Retrieval-Augmented Generation) diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor b/app/MindWork AI Studio/Components/AssistantBlock.razor index 754a9e24..372fae7e 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor @@ -1,30 +1,33 @@ @inherits MSGComponentBase @typeparam TSettings - - - - - - - @this.Name +@if (this.IsVisible) +{ + + + + + + + @this.Name + + + + + + + + @this.Description - - - - - - @this.Description - - - - - - - @this.ButtonText - - - - - \ No newline at end of file + + + + + @this.ButtonText + + + + + +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs index ef0e7a4d..69dfe49b 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs @@ -1,3 +1,5 @@ +using AIStudio.Settings.DataModel; + using Microsoft.AspNetCore.Components; using DialogOptions = AIStudio.Dialogs.DialogOptions; @@ -8,32 +10,38 @@ public partial class AssistantBlock : MSGComponentBase where TSetting { [Parameter] public string Name { get; set; } = string.Empty; - + [Parameter] public string Description { get; set; } = string.Empty; - + [Parameter] public string Icon { get; set; } = Icons.Material.Filled.DisabledByDefault; - + [Parameter] public string ButtonText { get; set; } = "Start"; - + [Parameter] public string Link { get; set; } = string.Empty; - + + [Parameter] + public Tools.Components Component { get; set; } = Tools.Components.NONE; + + [Parameter] + public PreviewFeatures RequiredPreviewFeature { get; set; } = PreviewFeatures.NONE; + [Inject] private MudTheme ColorTheme { get; init; } = null!; - + [Inject] private IDialogService DialogService { get; init; } = null!; private async Task OpenSettingsDialog() { var dialogParameters = new DialogParameters(); - + await this.DialogService.ShowAsync(T("Open Settings"), dialogParameters, DialogOptions.FULLSCREEN); } - + private string BorderColor => this.SettingsManager.IsDarkMode switch { true => this.ColorTheme.GetCurrentPalette(this.SettingsManager).GrayLight, @@ -41,4 +49,6 @@ public partial class AssistantBlock : MSGComponentBase where TSetting }; private string BlockStyle => $"border-width: 2px; border-color: {this.BorderColor}; border-radius: 12px; border-style: solid; max-width: 20em;"; + + private bool IsVisible => this.SettingsManager.IsAssistantVisible(this.Component, assistantName: this.Name, requiredPreviewFeature: this.RequiredPreviewFeature); } \ No newline at end of file diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor index 1cd72a5f..250aa06c 100644 --- a/app/MindWork AI Studio/Pages/Assistants.razor +++ b/app/MindWork AI Studio/Pages/Assistants.razor @@ -9,60 +9,88 @@ - - - @T("General") - - - - - - - - - - @T("Business") - - - - - @if (PreviewFeatures.PRE_DOCUMENT_ANALYSIS_2025.IsEnabled(this.SettingsManager)) - { - - } - - - - - - - + @if (this.SettingsManager.IsAnyCategoryAssistantVisible("General", + (Components.TEXT_SUMMARIZER_ASSISTANT, PreviewFeatures.NONE), + (Components.TRANSLATION_ASSISTANT, PreviewFeatures.NONE), + (Components.GRAMMAR_SPELLING_ASSISTANT, PreviewFeatures.NONE), + (Components.REWRITE_ASSISTANT, PreviewFeatures.NONE), + (Components.SYNONYMS_ASSISTANT, PreviewFeatures.NONE) + )) + { + + @T("General") + + + + + + + + + } - - @T("Learning") - - - - - - - @T("Software Engineering") - - - - @if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager)) - { - - } - - - - @T("AI Studio Development") - - - - + @if (this.SettingsManager.IsAnyCategoryAssistantVisible("Business", + (Components.EMAIL_ASSISTANT, PreviewFeatures.NONE), + (Components.DOCUMENT_ANALYSIS_ASSISTANT, PreviewFeatures.PRE_DOCUMENT_ANALYSIS_2025), + (Components.MY_TASKS_ASSISTANT, PreviewFeatures.NONE), + (Components.AGENDA_ASSISTANT, PreviewFeatures.NONE), + (Components.JOB_POSTING_ASSISTANT, PreviewFeatures.NONE), + (Components.LEGAL_CHECK_ASSISTANT, PreviewFeatures.NONE), + (Components.ICON_FINDER_ASSISTANT, PreviewFeatures.NONE) + )) + { + + @T("Business") + + + + + + + + + + + } + + @if (this.SettingsManager.IsAnyCategoryAssistantVisible("Learning", + (Components.BIAS_DAY_ASSISTANT, PreviewFeatures.NONE) + )) + { + + @T("Learning") + + + + + } + + @if (this.SettingsManager.IsAnyCategoryAssistantVisible("Software Engineering", + (Components.CODING_ASSISTANT, PreviewFeatures.NONE), + (Components.ERI_ASSISTANT, PreviewFeatures.PRE_RAG_2024) + )) + { + + @T("Software Engineering") + + + + + + } + + @if (this.SettingsManager.IsAnyCategoryAssistantVisible("AI Studio Development", + (Components.I18N_ASSISTANT, PreviewFeatures.NONE) + )) + { + + @T("AI Studio Development") + + + + + } - \ No newline at end of file + diff --git a/app/MindWork AI Studio/Plugins/configuration/plugin.lua b/app/MindWork AI Studio/Plugins/configuration/plugin.lua index 8cd62555..9708c666 100644 --- a/app/MindWork AI Studio/Plugins/configuration/plugin.lua +++ b/app/MindWork AI Studio/Plugins/configuration/plugin.lua @@ -143,6 +143,16 @@ CONFIG["SETTINGS"] = {} -- Please note: using an empty string ("") will lock the selection and disable dictation/transcription. -- CONFIG["SETTINGS"]["DataApp.UseTranscriptionProvider"] = "00000000-0000-0000-0000-000000000000" +-- Configure which assistants should be hidden from the UI. +-- Allowed values are: +-- GRAMMAR_SPELLING_ASSISTANT, ICON_FINDER_ASSISTANT, REWRITE_ASSISTANT, +-- TRANSLATION_ASSISTANT, AGENDA_ASSISTANT, CODING_ASSISTANT, +-- TEXT_SUMMARIZER_ASSISTANT, EMAIL_ASSISTANT, LEGAL_CHECK_ASSISTANT, +-- SYNONYMS_ASSISTANT, MY_TASKS_ASSISTANT, JOB_POSTING_ASSISTANT, +-- BIAS_DAY_ASSISTANT, ERI_ASSISTANT, DOCUMENT_ANALYSIS_ASSISTANT, +-- I18N_ASSISTANT +-- CONFIG["SETTINGS"]["DataApp.HiddenAssistants"] = { "ERI_ASSISTANT", "I18N_ASSISTANT" } + -- Example chat templates for this configuration: CONFIG["CHAT_TEMPLATES"] = {} diff --git a/app/MindWork AI Studio/Settings/ConfigurableAssistant.cs b/app/MindWork AI Studio/Settings/ConfigurableAssistant.cs new file mode 100644 index 00000000..f1076d9e --- /dev/null +++ b/app/MindWork AI Studio/Settings/ConfigurableAssistant.cs @@ -0,0 +1,30 @@ +namespace AIStudio.Settings; + +/// +/// Enum representing assistants that can be hidden via configuration plugin. +/// +public enum ConfigurableAssistant +{ + NONE = 0, + UNKNOWN = 1, + + GRAMMAR_SPELLING_ASSISTANT, + ICON_FINDER_ASSISTANT, + REWRITE_ASSISTANT, + TRANSLATION_ASSISTANT, + AGENDA_ASSISTANT, + CODING_ASSISTANT, + TEXT_SUMMARIZER_ASSISTANT, + EMAIL_ASSISTANT, + LEGAL_CHECK_ASSISTANT, + SYNONYMS_ASSISTANT, + MY_TASKS_ASSISTANT, + JOB_POSTING_ASSISTANT, + BIAS_DAY_ASSISTANT, + ERI_ASSISTANT, + DOCUMENT_ANALYSIS_ASSISTANT, + + // ReSharper disable InconsistentNaming + I18N_ASSISTANT, + // ReSharper restore InconsistentNaming +} diff --git a/app/MindWork AI Studio/Settings/DataModel/DataApp.cs b/app/MindWork AI Studio/Settings/DataModel/DataApp.cs index 720d3b5a..d2ac4bb7 100644 --- a/app/MindWork AI Studio/Settings/DataModel/DataApp.cs +++ b/app/MindWork AI Studio/Settings/DataModel/DataApp.cs @@ -86,4 +86,9 @@ public sealed class DataApp(Expression>? configSelection = n /// Should the user be allowed to add providers? /// public bool AllowUserToAddProvider { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToAddProvider, true); + + /// + /// List of assistants that should be hidden from the UI. + /// + public HashSet HiddenAssistants { get; set; } = ManagedConfiguration.Register(configSelection, n => n.HiddenAssistants, []); } \ No newline at end of file diff --git a/app/MindWork AI Studio/Settings/DataModel/PreviewFeatures.cs b/app/MindWork AI Studio/Settings/DataModel/PreviewFeatures.cs index d74898dd..e58ecdca 100644 --- a/app/MindWork AI Studio/Settings/DataModel/PreviewFeatures.cs +++ b/app/MindWork AI Studio/Settings/DataModel/PreviewFeatures.cs @@ -2,6 +2,8 @@ namespace AIStudio.Settings.DataModel; public enum PreviewFeatures { + NONE = 0, + // // Important: Never delete any enum value from this list. // We must be able to deserialize old settings files that may contain these values. diff --git a/app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs b/app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs new file mode 100644 index 00000000..cb4e7fdc --- /dev/null +++ b/app/MindWork AI Studio/Tools/AssistantVisibilityExtensions.cs @@ -0,0 +1,100 @@ +using AIStudio.Settings; +using AIStudio.Settings.DataModel; + +namespace AIStudio.Tools; + +/// +/// Extension methods for checking assistant visibility based on configuration and preview features. +/// +public static class AssistantVisibilityExtensions +{ + private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(nameof(AssistantVisibilityExtensions)); + + /// + /// Checks if an assistant should be visible based on configuration and optional preview feature requirements. + /// + /// The settings manager to check configuration against. + /// Whether to log visibility decisions. + /// The name of the assistant to check (for logging purposes). + /// The assistant component to check. + /// Optional preview feature that must be enabled for the assistant to be visible. + /// True if the assistant should be visible, false otherwise. + public static bool IsAssistantVisible(this SettingsManager settingsManager, Components component, bool withLogging = true, string assistantName = "", PreviewFeatures requiredPreviewFeature = PreviewFeatures.NONE) + { + withLogging = withLogging && !string.IsNullOrWhiteSpace(assistantName); + + // Check if a preview feature is required and enabled: + if (requiredPreviewFeature != PreviewFeatures.NONE && !requiredPreviewFeature.IsEnabled(settingsManager)) + { + if(withLogging) + LOGGER.LogInformation("Assistant '{AssistantName}' is not visible because the required preview feature '{PreviewFeature}' is not enabled.", assistantName, requiredPreviewFeature); + + return false; + } + + // If no component is specified, it's always visible: + if (component is Components.NONE) + { + if(withLogging) + LOGGER.LogWarning("Assistant '{AssistantName}' is visible because no component is specified.", assistantName); + + return true; + } + + // Map Components enum to ConfigurableAssistant enum: + var configurableAssistant = component switch + { + Components.GRAMMAR_SPELLING_ASSISTANT => ConfigurableAssistant.GRAMMAR_SPELLING_ASSISTANT, + Components.ICON_FINDER_ASSISTANT => ConfigurableAssistant.ICON_FINDER_ASSISTANT, + Components.REWRITE_ASSISTANT => ConfigurableAssistant.REWRITE_ASSISTANT, + Components.TRANSLATION_ASSISTANT => ConfigurableAssistant.TRANSLATION_ASSISTANT, + Components.AGENDA_ASSISTANT => ConfigurableAssistant.AGENDA_ASSISTANT, + Components.CODING_ASSISTANT => ConfigurableAssistant.CODING_ASSISTANT, + Components.TEXT_SUMMARIZER_ASSISTANT => ConfigurableAssistant.TEXT_SUMMARIZER_ASSISTANT, + Components.EMAIL_ASSISTANT => ConfigurableAssistant.EMAIL_ASSISTANT, + Components.LEGAL_CHECK_ASSISTANT => ConfigurableAssistant.LEGAL_CHECK_ASSISTANT, + Components.SYNONYMS_ASSISTANT => ConfigurableAssistant.SYNONYMS_ASSISTANT, + Components.MY_TASKS_ASSISTANT => ConfigurableAssistant.MY_TASKS_ASSISTANT, + Components.JOB_POSTING_ASSISTANT => ConfigurableAssistant.JOB_POSTING_ASSISTANT, + Components.BIAS_DAY_ASSISTANT => ConfigurableAssistant.BIAS_DAY_ASSISTANT, + Components.ERI_ASSISTANT => ConfigurableAssistant.ERI_ASSISTANT, + Components.DOCUMENT_ANALYSIS_ASSISTANT => ConfigurableAssistant.DOCUMENT_ANALYSIS_ASSISTANT, + Components.I18N_ASSISTANT => ConfigurableAssistant.I18N_ASSISTANT, + + _ => ConfigurableAssistant.UNKNOWN, + }; + + // If the component doesn't map to a configurable assistant, it's always visible: + if (configurableAssistant is ConfigurableAssistant.UNKNOWN) + { + if(withLogging) + LOGGER.LogWarning("Assistant '{AssistantName}' is visible because its component '{Component}' does not map to a configurable assistant.", assistantName, component); + + return true; + } + + // Check if the assistant is hidden by any configuration plugin: + var isHidden = settingsManager.ConfigurationData.App.HiddenAssistants.Contains(configurableAssistant); + if (isHidden && withLogging) + LOGGER.LogInformation("Assistant '{AssistantName}' is hidden based on the configuration.", assistantName); + + return !isHidden; + } + + /// + /// Checks if any assistant in a category should be visible. + /// + /// The settings manager to check configuration against. + /// The name of the assistant category (for logging purposes). + /// The assistants in the category with their optional preview feature requirements. + /// True if at least one assistant in the category should be visible, false otherwise. + public static bool IsAnyCategoryAssistantVisible(this SettingsManager settingsManager, string categoryName, params (Components Component, PreviewFeatures RequiredPreviewFeature)[] assistants) + { + foreach (var (component, requiredPreviewFeature) in assistants) + if (settingsManager.IsAssistantVisible(component, withLogging: false, requiredPreviewFeature: requiredPreviewFeature)) + return true; + + LOGGER.LogInformation("No assistants in category '{CategoryName}' are visible.", categoryName); + return false; + } +} diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs index 76148218..814aa971 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginConfiguration.cs @@ -67,6 +67,9 @@ public sealed class PluginConfiguration(bool isInternal, LuaState state, PluginT // Config: enabled preview features ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.EnabledPreviewFeatures, this.Id, settingsTable, dryRun); + // Config: hide some assistants? + ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.HiddenAssistants, this.Id, settingsTable, dryRun); + // Handle configured LLM providers: PluginConfigurationObject.TryParse(PluginConfigurationObjectType.LLM_PROVIDER, x => x.Providers, x => x.NextProviderNum, mainTable, this.Id, ref this.configObjects, dryRun); diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs index 2efbde97..63eb3df5 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs @@ -177,6 +177,10 @@ public static partial class PluginFactory if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.UseTranscriptionProvider, AVAILABLE_PLUGINS)) wasConfigurationChanged = true; + // Check for hidden assistants: + if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.HiddenAssistants, AVAILABLE_PLUGINS)) + wasConfigurationChanged = true; + if (wasConfigurationChanged) { await SETTINGS_MANAGER.StoreSettings(); diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.1.2.md b/app/MindWork AI Studio/wwwroot/changelog/v26.1.2.md index 4e5fd360..b05871b0 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v26.1.2.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v26.1.2.md @@ -1 +1,2 @@ # v26.1.2, build 232 (2026-01-xx xx:xx UTC) +- Added the option to hide specific assistants by configuration plugins. This is useful for enterprise environments in organizations. \ No newline at end of file