diff --git a/AGENTS.md b/AGENTS.md
index d559c62e..c9891d61 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -29,14 +29,44 @@ dotnet run build
```
This builds the .NET app as a Tauri "sidecar" binary, which is required even for development.
-### Running .NET builds from an agent
-- Do not run `.NET` builds such as `dotnet run build`, `dotnet build`, or similar build commands from an agent. Codex agents can hit a known sandbox issue during `.NET` builds, typically surfacing as `CSSM_ModuleLoad()` or other sandbox-related failures.
-- Instead, ask the user to run the `.NET` build locally in their IDE and report the result back.
-- Recommend the canonical repo build flow for the user: open an IDE terminal in the repository and run `cd app/Build && dotnet run build`.
-- If the context fits better, it is also acceptable to ask the user to start the build using their IDE's built-in build action, as long as it is clear the build must be run locally by the user.
-- After asking for the build, wait for the user's feedback before diagnosing issues, making follow-up changes, or suggesting the next step.
-- Treat the user's build output, error messages, or success confirmation as the source of truth for further troubleshooting.
-- For reference: https://github.com/openai/codex/issues/4915
+### Running builds from an agent
+Agents must not start builds through their own shell: agent shells run sandboxed, and `.NET` builds
+hit a known sandbox issue there, typically surfacing as `CSSM_ModuleLoad()` or other sandbox-related
+failures (for reference: https://github.com/openai/codex/issues/4915). This applies to `dotnet run build`,
+`dotnet build`, `cargo build`, and similar commands.
+
+Instead, use the JetBrains IDE MCP servers. They execute in the IDE process, which runs outside the
+agent sandbox:
+
+- `rider` for the .NET solution at `app/MindWork AI Studio.sln`
+- `rustrover` for the Rust runtime at `runtime/`
+
+Pass the `rootFolder` parameter on every call, e.g. the absolute path of the `app` directory for Rider
+and of `runtime` for RustRover. It avoids ambiguous calls when several IDE windows are open.
+
+**Compile check of the .NET code:** start `mcp__rider__build_solution_start`, then poll
+`mcp__rider__build_solution_state` until its state is `Completed` and read `buildIsSuccess` plus the
+collected problems. `mcp__rider__get_project_problems` reports the current Problems View without
+triggering a new build.
+
+**Build script commands** such as the canonical build or the I18N collection run through the IDE
+terminal, because they are more than a solution build:
+
+```
+mcp__rider__execute_terminal_command command: "cd app/Build && dotnet run build"
+mcp__rider__execute_terminal_command command: "cd app/Build && dotnet run collect-i18n"
+```
+
+**Rust builds** work the same way through the matching `rustrover` tools.
+
+Notes:
+- The IDE may ask the user to confirm a terminal command. Wait for the result instead of retrying the
+ command in the agent shell.
+- When the IDE is not running or its MCP server is unavailable, fall back to asking the user to run
+ `cd app/Build && dotnet run build` locally, and wait for their feedback before diagnosing issues or
+ making follow-up changes.
+- Treat the build output, error messages, or success confirmation as the source of truth for further
+ troubleshooting, no matter whether it came from the MCP server or from the user.
### Running Tests
Currently, no automated test suite exists in the repository.
@@ -113,12 +143,12 @@ Plugins can configure:
- etc.
Configuration plugins provide three kinds of values:
-- **Managed settings:** simple values such as booleans, numbers, strings, enums, lists, or sets handled through `ManagedConfiguration`. These values may be locked or used as organization defaults.
+- **Managed settings:** simple values such as booleans, numbers, strings, enums, lists, or sets handled through `ManagedConfiguration`. These values may be locked or used as organization defaults. Which configuration plugin owns a locked setting is persisted in `Data.ManagedLockedConfigurations`, and organization defaults are tracked in `Data.ManagedEditableDefaults`. Both are cleaned up generically by `ManagedConfiguration.CleanupLeftOverManagedConfigurations(...)` when the owning plugin is gone. The value a setting had before a configuration plugin took it over is kept in `Data.ManagedUserValueSnapshots` and restored by that same clean-up, so removing a plugin hands the user's own value back instead of the app default.
- **Managed configuration objects:** complex Lua tables that are persisted into `SettingsManager.ConfigurationData`, implement `IConfigurationObject`, and are cleaned up through `PluginConfigurationObject.CleanLeftOverConfigurationObjects(...)`. Examples include providers, profiles, chat templates, data sources, and document analysis policies.
- **Live plugin content:** complex Lua tables that implement `ILivePluginContent` and are read live from running plugins instead of being persisted to `ConfigurationData`. Examples include `MANDATORY_INFOS` and `INTRODUCTIONS`. If live plugin content creates persistent side data, add a dedicated cleanup path for that side data, like mandatory-info acceptances.
When adding configuration plugin capabilities:
-- For managed settings, update the corresponding data class in `app/MindWork AI Studio/Settings/DataModel/` to call `ManagedConfiguration.Register(...)`, process the setting in `PluginConfiguration.TryProcessConfiguration`, and check for leftover managed configuration in `PluginFactory.Loading.LoadAll`.
+- For managed settings, update the corresponding data class in `app/MindWork AI Studio/Settings/DataModel/` to call `ManagedConfiguration.Register(...)` and process the setting in `PluginConfiguration.TryProcessConfiguration`. Cleaning up the setting when its configuration plugin was removed needs no extra step: `ManagedConfiguration.CleanupLeftOverManagedConfigurations(...)` iterates all registered settings. Do not add per-setting cleanup calls to `PluginFactory.Loading.LoadAll`.
- For managed configuration objects, update `PluginConfigurationObject.cs` and `PluginConfigurationObjectType.cs`, persist them in the appropriate `ConfigurationData` collection, and add cleanup via `PluginConfigurationObject.CleanLeftOverConfigurationObjects(...)`.
- For live plugin content, add a data type implementing `ILivePluginContent`, parse it in `PluginConfiguration`, expose it through `PluginFactory`, and add any required cleanup only for persistent side data.
- Always document the new capability in `app/MindWork AI Studio/Plugins/configuration/plugin.lua`.
@@ -193,7 +223,7 @@ Multi-level confidence scheme allows users to control which providers see which
- **No automated formatting for Rust or .NET files** - Never run automated formatters on Rust files (`.rs`) or .NET files (`.cs`, `.razor`, `.csproj`, etc.). Only make the minimal manual formatting changes required for the specific edit.
- **I18N resources are generated** - Do not manually edit `app/MindWork AI Studio/Assistants/I18N/allTexts.lua`, `app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua`, or `app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua`. These files are updated automatically by the I18N process.
- **Spaces in paths** - Always quote paths with spaces in bash commands
-- **Agent-run .NET builds** - Do not run `.NET` builds from an agent. Ask the user to run the build locally in their IDE, preferably via `cd app/Build && dotnet run build` in an IDE terminal, then wait for their feedback before continuing.
+- **Agent-run builds** - Never start `.NET` or Rust builds in the agent's own shell; it is sandboxed. Use the `rider` and `rustrover` MCP servers instead, which build in the IDE outside that sandbox. See "Running builds from an agent" above.
- **Debug environment** - Reads `startup.env` file with IPC credentials
- **Production environment** - Runtime launches .NET sidecar with environment variables
- **MudBlazor** - Component library requires DI setup in Program.cs
diff --git a/app/.codex/config.toml b/app/.codex/config.toml
new file mode 100644
index 00000000..5f9e6911
--- /dev/null
+++ b/app/.codex/config.toml
@@ -0,0 +1,2 @@
+ [mcp_servers.rider]
+ url = "http://127.0.0.1:64482/stream"
diff --git a/app/Build/Build Script.csproj b/app/Build/Build Script.csproj
index 5694b509..5a184f2d 100644
--- a/app/Build/Build Script.csproj
+++ b/app/Build/Build Script.csproj
@@ -12,6 +12,9 @@
+
+
+
diff --git a/app/Directory.Build.props b/app/Directory.Build.props
new file mode 100644
index 00000000..a3c7e870
--- /dev/null
+++ b/app/Directory.Build.props
@@ -0,0 +1,8 @@
+
+
+
+
+ all
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
index a7ebe2eb..5746ff62 100644
--- a/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
+++ b/app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
@@ -659,9 +659,12 @@ public abstract partial class AssistantBase : AssistantLowerBase wher
await this.AssistantSessionService.ClearAsync(this.assistantSessionKey);
this.MediaTranscriptionService.ClearOwnerState(this.CurrentMediaImportOwner);
this.assistantSessionId = null;
+ this.ChatThread = null;
+ this.LastUserPrompt = null;
this.ResultingContentBlock = null;
this.ProviderSettings = Settings.Provider.NONE;
+ await this.JsRuntime.ClearDiv(BEFORE_RESULT_DIV_ID);
await this.JsRuntime.ClearDiv(RESULT_DIV_ID);
await this.JsRuntime.ClearDiv(AFTER_RESULT_DIV_ID);
diff --git a/app/MindWork AI Studio/Assistants/Builder/AssistantBuilder.razor.cs b/app/MindWork AI Studio/Assistants/Builder/AssistantBuilder.razor.cs
index d99feb36..42482f07 100644
--- a/app/MindWork AI Studio/Assistants/Builder/AssistantBuilder.razor.cs
+++ b/app/MindWork AI Studio/Assistants/Builder/AssistantBuilder.razor.cs
@@ -17,7 +17,7 @@ public partial class AssistantBuilder : AssistantBaseCore
private IDialogService DialogService { get; init; } = null!;
[Inject]
- private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!;
+ private PluginInstallService PluginInstallService { get; init; } = null!;
[Inject]
private AssistantPluginGenerationService AssistantPluginGenerationService { get; init; } = null!;
@@ -500,7 +500,7 @@ public partial class AssistantBuilder : AssistantBaseCore
this.isCheckingPlugin = true;
try
{
- var result = await this.AssistantPluginInstallService.CheckInstallabilityAsync(this.generatedLuaAssistant, CancellationToken.None);
+ var result = await this.PluginInstallService.CheckInstallabilityAsync(this.generatedLuaAssistant, CancellationToken.None);
this.pluginCheckResult = result;
if (!result.Success)
{
@@ -530,7 +530,7 @@ public partial class AssistantBuilder : AssistantBaseCore
this.isInstallingPlugin = true;
try
{
- var result = await this.AssistantPluginInstallService.InstallAsync(this.generatedLuaAssistant, CancellationToken.None);
+ var result = await this.PluginInstallService.InstallAsync(this.generatedLuaAssistant, CancellationToken.None);
this.pluginInstallResult = result;
if (!result.Success)
{
diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
index dfe94618..150a0466 100644
--- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
+++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua
@@ -2677,6 +2677,126 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGBUILDPROGRE
-- Build progress
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGBUILDPROGRESS::T909046610"] = "Build progress"
+-- The model did not fill every planned content slot exactly once. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1003911239"] = "The model did not fill every planned content slot exactly once. Please try again or select another model."
+
+-- The sources of this briefing could not be prepared.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1034452233"] = "The sources of this briefing could not be prepared."
+
+-- This operation did not change the briefing, so no new version was created.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1058618049"] = "This operation did not change the briefing, so no new version was created."
+
+-- The model filled a content slot with the wrong kind of value. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1099589813"] = "The model filled a content slot with the wrong kind of value. Please try again or select another model."
+
+-- The model response contained an empty, malformed, or duplicated identifier. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1198458597"] = "The model response contained an empty, malformed, or duplicated identifier. Please try again or select another model."
+
+-- The model did not cover every source of this briefing exactly once. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1209705994"] = "The model did not cover every source of this briefing exactly once. Please try again or select another model."
+
+-- An accessibility text of the model response was empty or invalid. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1437512295"] = "An accessibility text of the model response was empty or invalid. Please try again or select another model."
+
+-- The model response used a prohibited attribute. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1677678770"] = "The model response used a prohibited attribute. Please try again or select another model."
+
+-- A chart of the model response contained invalid categories or data series. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T181588270"] = "A chart of the model response contained invalid categories or data series. Please try again or select another model."
+
+-- A source of this briefing can no longer be reached. Please relink or remove the affected source.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1878061605"] = "A source of this briefing can no longer be reached. Please relink or remove the affected source."
+
+-- The selected provider could not complete this briefing stage.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1905087799"] = "The selected provider could not complete this briefing stage."
+
+-- A calculation of the model response used an invalid operation. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T1992964953"] = "A calculation of the model response used an invalid operation. Please try again or select another model."
+
+-- The model response did not match the required contract. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T214297315"] = "The model response did not match the required contract. Please try again or select another model."
+
+-- The model response contained unexpected fields. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2192261405"] = "The model response contained unexpected fields. Please try again or select another model."
+
+-- AI Studio was closed while this briefing was being built. You can resume the build.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2197645770"] = "AI Studio was closed while this briefing was being built. You can resume the build."
+
+-- The presentation of the model response did not match the briefing contract. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2376983148"] = "The presentation of the model response did not match the briefing contract. Please try again or select another model."
+
+-- This visual briefing operation was canceled.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T240791538"] = "This visual briefing operation was canceled."
+
+-- The model response contained markup or code, which this briefing does not allow. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2529598303"] = "The model response contained markup or code, which this briefing does not allow. Please try again or select another model."
+
+-- AI Studio compiled this briefing into an inconsistent result. Please copy the technical details and report this issue.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2668127220"] = "AI Studio compiled this briefing into an inconsistent result. Please copy the technical details and report this issue."
+
+-- This briefing could not be assembled.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2678882954"] = "This briefing could not be assembled."
+
+-- An interactive control of the model response targeted an invalid briefing element. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2714042531"] = "An interactive control of the model response targeted an invalid briefing element. Please try again or select another model."
+
+-- The model did not return valid JSON. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2784808603"] = "The model did not return valid JSON. Please try again or select another model."
+
+-- A calculation of the model response targeted an invalid briefing element. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2795934353"] = "A calculation of the model response targeted an invalid briefing element. Please try again or select another model."
+
+-- An interactive control of the model response used an invalid initial state. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2796279475"] = "An interactive control of the model response used an invalid initial state. Please try again or select another model."
+
+-- The accessibility texts of the model response did not match the briefing elements. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2815870761"] = "The accessibility texts of the model response did not match the briefing elements. Please try again or select another model."
+
+-- The new version of this briefing could not be saved.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2818947691"] = "The new version of this briefing could not be saved."
+
+-- The model did not plan every visual asset of this briefing exactly once. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T2853629903"] = "The model did not plan every visual asset of this briefing exactly once. Please try again or select another model."
+
+-- The assembled briefing did not pass the security validation.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T295498807"] = "The assembled briefing did not pass the security validation."
+
+-- The charts of the model response did not match the planned briefing elements. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T3326200304"] = "The charts of the model response did not match the planned briefing elements. Please try again or select another model."
+
+-- An interactive control of the model response used an invalid identifier. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T3412185985"] = "An interactive control of the model response used an invalid identifier. Please try again or select another model."
+
+-- The model response referenced content that does not exist. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T344215744"] = "The model response referenced content that does not exist. Please try again or select another model."
+
+-- The updated content no longer fits the current presentation. You can continue as a rebuild.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T3515116214"] = "The updated content no longer fits the current presentation. You can continue as a rebuild."
+
+-- The model response contained a value of the wrong type. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T3668896836"] = "The model response contained a value of the wrong type. Please try again or select another model."
+
+-- This briefing has no provider selected. Please select a provider before you generate a briefing.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T3834145318"] = "This briefing has no provider selected. Please select a provider before you generate a briefing."
+
+-- The selected model lacks a capability this briefing needs. Please select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T4066127340"] = "The selected model lacks a capability this briefing needs. Please select another model."
+
+-- A media transcript of this briefing is missing or outdated. Please transcribe the affected media again.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T449544952"] = "A media transcript of this briefing is missing or outdated. Please transcribe the affected media again."
+
+-- The model response used an invalid briefing layout. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T686008237"] = "The model response used an invalid briefing layout. Please try again or select another model."
+
+-- A briefing element of the model response was missing its required interactive controls. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T762236598"] = "A briefing element of the model response was missing its required interactive controls. Please try again or select another model."
+
+-- This visual briefing operation failed because of an unexpected internal error. Please copy the technical details for support.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T875151112"] = "This visual briefing operation failed because of an unexpected internal error. Please copy the technical details for support."
+
+-- The model response used an unsupported contract version. Please try again or select another model.
+UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::VISUALBRIEFING::VISUALBRIEFINGFAILUREEXTENSIONS::T921285247"] = "The model response used an unsupported contract version. Please try again or select another model."
+
-- This chart cannot be displayed: {0}
UI_TEXT_CONTENT["AISTUDIO::CHAT::CHARTBLOCK::T1070038198"] = "This chart cannot be displayed: {0}"
@@ -2797,24 +2917,6 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T3571008422"] = "Assistan
-- The result is ready.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T661906146"] = "The result is ready."
--- The assistant cannot be deleted while background work is still running.
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1318944584"] = "The assistant cannot be deleted while background work is still running."
-
--- Delete assistant plugin
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1692493145"] = "Delete assistant plugin"
-
--- Delete Assistant Plugin
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3637071001"] = "Delete Assistant Plugin"
-
--- The '{0}' assistant plugin has been successfully removed.
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3815023384"] = "The '{0}' assistant plugin has been successfully removed."
-
--- The assistant plugin '{0}' could not be deleted: {1}
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3985264168"] = "The assistant plugin '{0}' could not be deleted: {1}"
-
--- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files.
-UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T4033722845"] = "Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files."
-
-- Show or hide the detailed security information.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T1045105126"] = "Show or hide the detailed security information."
@@ -3262,6 +3364,39 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MUDCOPYCLIPBOARDBUTTON::T12948066"] = "Co
-- Cannot copy this content type to clipboard.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::MUDCOPYCLIPBOARDBUTTON::T3937637647"] = "Cannot copy this content type to clipboard."
+-- The assistant cannot be deleted while background work is still running.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T1318944584"] = "The assistant cannot be deleted while background work is still running."
+
+-- Delete assistant plugin
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T1692493145"] = "Delete assistant plugin"
+
+-- Do you really want to delete the language plugin '{0}'? This permanently deletes its local plugin files. When it is your chosen language, AI Studio returns to choosing the language automatically.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T1744561175"] = "Do you really want to delete the language plugin '{0}'? This permanently deletes its local plugin files. When it is your chosen language, AI Studio returns to choosing the language automatically."
+
+-- Delete language plugin
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T2707495447"] = "Delete language plugin"
+
+-- The plugin '{0}' could not be deleted: {1}
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T2738963920"] = "The plugin '{0}' could not be deleted: {1}"
+
+-- Delete Language Plugin
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T2990518039"] = "Delete Language Plugin"
+
+-- Delete Configuration Plugin
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T3395354991"] = "Delete Configuration Plugin"
+
+-- The plugin '{0}' has been successfully removed.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T3476138264"] = "The plugin '{0}' has been successfully removed."
+
+-- Delete Assistant Plugin
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T3637071001"] = "Delete Assistant Plugin"
+
+-- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T4033722845"] = "Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files."
+
+-- Delete configuration plugin
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PLUGINDELETEACTION::T459830575"] = "Delete configuration plugin"
+
-- Alpha phase means that we are working on the last details before the beta phase.
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::PREVIEWALPHA::T166807685"] = "Alpha phase means that we are working on the last details before the beta phase."
@@ -3874,6 +4009,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T40680
-- Edit Embedding Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T4264602229"] = "Edit Embedding Provider"
+-- This self-hosted embedding provider is trusted for data source security checks. Local data can be sent to it without security warnings.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T438107040"] = "This self-hosted embedding provider is trusted for data source security checks. Local data can be sent to it without security warnings."
+
-- Configure Embedding Providers
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELEMBEDDINGS::T488419116"] = "Configure Embedding Providers"
@@ -3958,6 +4096,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T386503
-- Delete LLM Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T4269256234"] = "Delete LLM Provider"
+-- This self-hosted provider is trusted for data source security checks.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T485526152"] = "This self-hosted provider is trusted for data source security checks."
+
-- Open Dashboard
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELPROVIDERS::T78223861"] = "Open Dashboard"
@@ -3985,6 +4126,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T17
-- Add Transcription Provider
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2066315685"] = "Add Transcription Provider"
+-- This self-hosted transcription provider is trusted for data source security checks.
+UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2175189736"] = "This self-hosted transcription provider is trusted for data source security checks."
+
-- Model
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::SETTINGS::SETTINGSPANELTRANSCRIPTION::T2189814010"] = "Model"
@@ -4624,6 +4768,84 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T823785464"] = "Allow th
-- Cancel
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CHATTEMPLATEDIALOG::T900713019"] = "Cancel"
+-- {0} LLM providers
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T121235760"] = "{0} LLM providers"
+
+-- {0} profiles
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T1238255445"] = "{0} profiles"
+
+-- No
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T1642511898"] = "No"
+
+-- {0} introductions on the welcome page
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2107991661"] = "{0} introductions on the welcome page"
+
+-- {0} mandatory information
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2150386772"] = "{0} mandatory information"
+
+-- You can install the plugin again later, but any changes you made to its settings are lost.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2156367745"] = "You can install the plugin again later, but any changes you made to its settings are lost."
+
+-- {0} profile
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2342765572"] = "{0} profile"
+
+-- {0} introduction on the welcome page
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2426110502"] = "{0} introduction on the welcome page"
+
+-- {0} embedding providers
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2438407498"] = "{0} embedding providers"
+
+-- Yes, delete it
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2466176832"] = "Yes, delete it"
+
+-- This also removes everything the configuration plugin had set up:
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T264970454"] = "This also removes everything the configuration plugin had set up:"
+
+-- {0} transcription provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T2681055470"] = "{0} transcription provider"
+
+-- {0} chat templates
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T3235448458"] = "{0} chat templates"
+
+-- {0} document analysis policy
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T3278137746"] = "{0} document analysis policy"
+
+-- The configuration plugin is not running, so we cannot tell what it had set up. Anything it configured will be removed as well.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T330559934"] = "The configuration plugin is not running, so we cannot tell what it had set up. Anything it configured will be removed as well."
+
+-- {0} LLM provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T3410030691"] = "{0} LLM provider"
+
+-- Do you really want to delete the configuration plugin '{0}'? This permanently deletes its local plugin files.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T3616855807"] = "Do you really want to delete the configuration plugin '{0}'? This permanently deletes its local plugin files."
+
+-- {0} settings return to their default values
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T3841220170"] = "{0} settings return to their default values"
+
+-- {0} setting returns to its default value
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T384701293"] = "{0} setting returns to its default value"
+
+-- {0} mandatory informations
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T3971735909"] = "{0} mandatory informations"
+
+-- {0} chat template
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T4147879421"] = "{0} chat template"
+
+-- {0} data sources, including their credentials in your operating system's keychain
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T4193757254"] = "{0} data sources, including their credentials in your operating system's keychain"
+
+-- {0} document analysis policies
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T449490978"] = "{0} document analysis policies"
+
+-- {0} data source, including its credentials in your operating system's keychain
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T511418335"] = "{0} data source, including its credentials in your operating system's keychain"
+
+-- {0} transcription providers
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T767586087"] = "{0} transcription providers"
+
+-- {0} embedding provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIGURATIONPLUGINDELETEDIALOG::T818101181"] = "{0} embedding provider"
+
-- No
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::CONFIRMDIALOG::T1642511898"] = "No"
@@ -5242,6 +5464,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGRESULTDIALOG::T1173984541"] = "Embe
-- Close
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::EMBEDDINGRESULTDIALOG::T3448155331"] = "Close"
+-- Close
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::INFORMATIONDIALOG::T3448155331"] = "Close"
+
-- Unfortunately, Pandoc's GPL license isn't compatible with the AI Studios licenses. However, software under the GPL is free to use and free of charge. You'll need to accept the GPL license before we can download and install Pandoc for you automatically (recommended). Alternatively, you might download it yourself using the instructions below or install it otherwise, e.g., by using a package manager of your operating system.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T1001483402"] = "Unfortunately, Pandoc's GPL license isn't compatible with the AI Studios licenses. However, software under the GPL is free to use and free of charge. You'll need to accept the GPL license before we can download and install Pandoc for you automatically (recommended). Alternatively, you might download it yourself using the instructions below or install it otherwise, e.g., by using a package manager of your operating system."
@@ -5332,6 +5557,117 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T504404155"] = "Accept the ter
-- Pandoc is distributed under the GNU General Public License v2 (GPL). By clicking "Accept GPL and archive," you agree to the terms of the GPL license. Software under GPL is free of charge and free to use.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PANDOCDIALOG::T523908375"] = "Pandoc is distributed under the GNU General Public License v2 (GPL). By clicking \"Accept GPL and archive,\" you agree to the terms of the GPL license. Software under GPL is free of charge and free to use."
+-- {0} profiles
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T1238255445"] = "{0} profiles"
+
+-- Install plugin
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T1525735539"] = "Install plugin"
+
+-- Version
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T1573770551"] = "Version"
+
+-- Source
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T1642243064"] = "Source"
+
+-- You are about to install a language plugin from a file.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T1974491324"] = "You are about to install a language plugin from a file."
+
+-- Authors
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T1985367263"] = "Authors"
+
+-- Data source
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2034620186"] = "Data source"
+
+-- A configuration takes effect right after the installation and has no on/off switch. Please check what it sets up:
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2051328106"] = "A configuration takes effect right after the installation and has no on/off switch. Please check what it sets up:"
+
+-- Plugins contain code that runs inside AI Studio. Install plugins only when you trust their source.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2053517490"] = "Plugins contain code that runs inside AI Studio. Install plugins only when you trust their source."
+
+-- You are about to install an assistant plugin from a file.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2063808316"] = "You are about to install an assistant plugin from a file."
+
+-- You are about to install a configuration plugin from a file.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T21052500"] = "You are about to install a configuration plugin from a file."
+
+-- {0} introductions on the welcome page
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2107991661"] = "{0} introductions on the welcome page"
+
+-- You are about to install a theme plugin from a file.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2163853103"] = "You are about to install a theme plugin from a file."
+
+-- {0} profile
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2342765572"] = "{0} profile"
+
+-- {0} introduction on the welcome page
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2426110502"] = "{0} introduction on the welcome page"
+
+-- Support contact
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2434966596"] = "Support contact"
+
+-- Name
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T266367750"] = "Name"
+
+-- {0} setting it takes control of
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T2868009192"] = "{0} setting it takes control of"
+
+-- {0} settings it takes control of
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3190775003"] = "{0} settings it takes control of"
+
+-- {0} chat templates
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3235448458"] = "{0} chat templates"
+
+-- {0} document analysis policy
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3278137746"] = "{0} document analysis policy"
+
+-- This replaces the already installed plugin '{0}'. Version {1} gets replaced by version {2}.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3415610475"] = "This replaces the already installed plugin '{0}'. Version {1} gets replaced by version {2}."
+
+-- Unknown
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3424652889"] = "Unknown"
+
+-- Type
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3512062061"] = "Type"
+
+-- {0} mandatory information you have to accept before using AI Studio
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3540986519"] = "{0} mandatory information you have to accept before using AI Studio"
+
+-- Transcription provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T3566003684"] = "Transcription provider"
+
+-- Replace plugin
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T4068580334"] = "Replace plugin"
+
+-- LLM provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T4099016901"] = "LLM provider"
+
+-- {0} chat template
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T4147879421"] = "{0} chat template"
+
+-- {0} document analysis policies
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T449490978"] = "{0} document analysis policies"
+
+-- The authors marked this plugin as deprecated: {0}
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T497068698"] = "The authors marked this plugin as deprecated: {0}"
+
+-- It also brings:
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T713968030"] = "It also brings:"
+
+-- You are about to install a plugin from a file.
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T841685558"] = "You are about to install a plugin from a file."
+
+-- Embedding provider
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T877326195"] = "Embedding provider"
+
+-- Cancel
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T900713019"] = "Cancel"
+
+-- Sends data to
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T914647109"] = "Sends data to"
+
+-- Destination
+UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PLUGINIMPORTDIALOG::T994314591"] = "Destination"
+
-- Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally.
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROFILEDIALOG::T1458195391"] = "Tell the AI what you want it to do for you. What are your goals or are you trying to achieve? Like having the AI address you informally."
@@ -7405,6 +7741,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1629800076"] = "Building on .NET
-- AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files.
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1630237140"] = "AI Studio creates a log file at startup, in which events during startup are recorded. After startup, another log file is created that records all events that occur during the use of the app. This includes any errors that may occur. Depending on when an error occurs (at startup or during use), the contents of these log files can be helpful for troubleshooting. Sensitive information such as passwords is not included in the log files."
+-- Plugin directory:
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1698127325"] = "Plugin directory:"
+
-- Consent:
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T171952677"] = "Consent:"
@@ -7435,6 +7774,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1924365263"] = "This library is
-- Encryption secret: is configured
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1931141322"] = "Encryption secret: is configured"
+-- The objc2 project provides access to Apple's Objective-C frameworks from Rust. On macOS, we use the libraries objc2, objc2-app-kit, and objc2-foundation to open the native macOS share sheet, e.g., when you share a plugin with others.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T1985806792"] = "The objc2 project provides access to Apple's Objective-C frameworks from Rust. On macOS, we use the libraries objc2, objc2-app-kit, and objc2-foundation to open the native macOS share sheet, e.g., when you share a plugin with others."
+
-- Copies the number of loaded root certificates to the clipboard
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2015329654"] = "Copies the number of loaded root certificates to the clipboard"
@@ -7444,6 +7786,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2029659664"] = "Copies the follo
-- Copies the server URL to the clipboard
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2037899437"] = "Copies the server URL to the clipboard"
+-- The windows-rs project provides access to Windows APIs from Rust. We use several libraries from this project: windows-registry is used to read the desired configuration in Windows enterprise environments. The windows and windows-collections libraries are used to open the native Windows share dialog, e.g., when you share a plugin with others.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2146481269"] = "The windows-rs project provides access to Windows APIs from Rust. We use several libraries from this project: windows-registry is used to read the desired configuration in Windows enterprise environments. The windows and windows-collections libraries are used to open the native Windows share dialog, e.g., when you share a plugin with others."
+
-- This library is used to create temporary folders in runtime tests and supporting filesystem operations.
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2160280545"] = "This library is used to create temporary folders in runtime tests and supporting filesystem operations."
@@ -7564,6 +7909,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3002755581"] = "This library ide
-- Changelog
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Changelog"
+-- Test configuration: nobody deployed this configuration. It is valid until you restart AI Studio.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3019585985"] = "Test configuration: nobody deployed this configuration. It is valid until you restart AI Studio."
+
-- External HTTPS custom root certificates are configured but not active.
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3021325354"] = "External HTTPS custom root certificates are configured but not active."
@@ -7579,6 +7927,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T313276297"] = "Connect AI Studio
-- Have feature ideas? Submit suggestions for future AI Studio enhancements.
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3178730036"] = "Have feature ideas? Submit suggestions for future AI Studio enhancements."
+-- Copies the plugin directory to the clipboard
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3182878147"] = "Copies the plugin directory to the clipboard"
+
-- Hide Details
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3183837919"] = "Hide Details"
@@ -7660,9 +8011,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3813932670"] = "this version doe
-- On Linux, ashpd provides access to desktop portals, allowing AI Studio to open folders and files for the user.
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3871176264"] = "On Linux, ashpd provides access to desktop portals, allowing AI Studio to open folders and files for the user."
--- This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration.
-UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3874337003"] = "This library is used to access the Windows registry. We use this for Windows enterprise environments to read the desired configuration."
-
-- Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json.
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3908558992"] = "Now we have multiple systems, some developed in .NET and others in Rust. The data format JSON is responsible for translating data between both worlds (called data serialization and deserialization). Serde takes on this task in the Rust world. The counterpart in the .NET world is an integral part of .NET and is located in System.Text.Json."
@@ -7705,6 +8053,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4158546761"] = "Community & Code
-- Executable path
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4164953312"] = "Executable path"
+-- AI Studio removed {0} test configuration(s) while starting. A test configuration is valid for one session: place it again while AI Studio is running.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4172838224"] = "AI Studio removed {0} test configuration(s) while starting. A test configuration is valid for one session: place it again while AI Studio is running."
+
-- We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant.
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T4184485147"] = "We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant."
@@ -7774,6 +8125,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "For some data tra
-- How to update
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T906183311"] = "How to update"
+-- A test configuration is active. It acts like a configuration of your organization and may, for example, approve assistant plugins. AI Studio removes it the next time you start the app.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T923110805"] = "A test configuration is active. It acts like a configuration of your organization and may, for example, approve assistant plugins. AI Studio removes it the next time you start the app."
+
-- Install Pandoc
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Install Pandoc"
@@ -7783,18 +8137,33 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1229643769"] = "Potentially Dangerou
-- Disable plugin
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1430375822"] = "Disable plugin"
+-- Import
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1463683828"] = "Import"
+
+-- Import plugin
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1467093263"] = "Import plugin"
+
-- Assistant Audit
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1506922856"] = "Assistant Audit"
-- Internal Plugins
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T158493184"] = "Internal Plugins"
+-- Plugin updated.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1646565893"] = "Plugin updated."
+
+-- Import plugin from a file
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T169921408"] = "Import plugin from a file"
+
-- Disabled Plugins
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1724138133"] = "Disabled Plugins"
-- Edit assistant plugin
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1851885496"] = "Edit assistant plugin"
+-- Plugin installed.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1889482678"] = "Plugin installed."
+
-- Send a mail
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1999487139"] = "Send a mail"
@@ -7816,18 +8185,45 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Enabled Plugins"
-- Revise Assistant Plugin
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T284393424"] = "Revise Assistant Plugin"
+-- Import not possible
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3051566124"] = "Import not possible"
+
-- The assistant plugin '{0}' has been successfully saved.
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3143506997"] = "The assistant plugin '{0}' has been successfully saved."
+-- An error occurred while sharing the plugin.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3184210266"] = "An error occurred while sharing the plugin."
+
+-- Your organization has disabled exporting plugins.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3342440765"] = "Your organization has disabled exporting plugins."
+
+-- Share plugin archive
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3355474457"] = "Share plugin archive"
+
+-- Your organization has disabled sharing plugins.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3379469503"] = "Your organization has disabled sharing plugins."
+
-- Close
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3448155331"] = "Close"
+-- Please drop a plugin archive with the extension {0} or .zip.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3785427568"] = "Please drop a plugin archive with the extension {0} or .zip."
+
-- Revise assistant plugin with AI
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3801095542"] = "Revise assistant plugin with AI"
-- Actions
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3865031940"] = "Actions"
+-- Export plugin archive
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3872669664"] = "Export plugin archive"
+
+-- Install Plugin
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3902690643"] = "Install Plugin"
+
+-- Please drop only one plugin archive at a time.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3974628410"] = "Please drop only one plugin archive at a time."
+
-- The automatic security audit for the assistant plugin '{0}' failed. Please run it manually.
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4066679817"] = "The automatic security audit for the assistant plugin '{0}' failed. Please run it manually."
@@ -7840,6 +8236,15 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4239378936"] = "Open website"
-- The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T448946658"] = "The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level \\\"{2}\\\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"
+-- The plugin archive was exported to '{0}'.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T659549952"] = "The plugin archive was exported to '{0}'."
+
+-- An error occurred while exporting the plugin.
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T759681732"] = "An error occurred while exporting the plugin."
+
+-- The plugin could not be imported: {0}
+UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T837269472"] = "The plugin could not be imported: {0}"
+
-- Settings
UI_TEXT_CONTENT["AISTUDIO::PAGES::SETTINGS::T1258653480"] = "Settings"
@@ -9253,6 +9658,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPES::T378481461"] = "Source like p
-- Document
UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPES::T4165204724"] = "Document"
+-- Plugin archive
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPES::T927001356"] = "Plugin archive"
+
-- The Assistant Builder context could not be loaded.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGINGENERATIONSERVICE::T108292972"] = "The Assistant Builder context could not be loaded."
@@ -9355,75 +9763,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGINGENERATIONSERVICE::T4
-- Please create an assistant draft first.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGINGENERATIONSERVICE::T4269176489"] = "Please create an assistant draft first."
--- Internal assistant plugins cannot be deleted.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1084244321"] = "Internal assistant plugins cannot be deleted."
-
--- The assistant plugin directory is outside the local assistant plugin directory.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1211881977"] = "The assistant plugin directory is outside the local assistant plugin directory."
-
--- Only assistant plugins can be edited.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1288328479"] = "Only assistant plugins can be edited."
-
--- The assistant cannot be deleted while background work is still running.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1318944584"] = "The assistant cannot be deleted while background work is still running."
-
--- No Lua plugin code was generated.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1839013358"] = "No Lua plugin code was generated."
-
--- The edited assistant plugin uses the ID of an internal AI Studio plugin.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2061233834"] = "The edited assistant plugin uses the ID of an internal AI Studio plugin."
-
--- The assistant plugin directory does not exist.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2148384567"] = "The assistant plugin directory does not exist."
-
--- The resolved plugin directory is outside the assistant plugin directory.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2223071618"] = "The resolved plugin directory is outside the assistant plugin directory."
-
--- Unexpected error: {0}
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2350673880"] = "Unexpected error: {0}"
-
--- The assistant plugin has no local directory.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2682912892"] = "The assistant plugin has no local directory."
-
--- The AI Studio data directory is not initialized yet.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2712481762"] = "The AI Studio data directory is not initialized yet."
-
--- Only assistant plugins can be deleted.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2864597027"] = "Only assistant plugins can be deleted."
-
--- The generated plugin is not an assistant plugin. Issue: {0}
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2955055168"] = "The generated plugin is not an assistant plugin. Issue: {0}"
-
--- The generated assistant plugin uses the ID of an internal AI Studio plugin.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3162363526"] = "The generated assistant plugin uses the ID of an internal AI Studio plugin."
-
--- Config Server managed assistant plugins cannot be deleted.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3751820312"] = "Config Server managed assistant plugins cannot be deleted."
-
--- Only assistants generated by the Assistant Builder can be deleted.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3940247198"] = "Only assistants generated by the Assistant Builder can be deleted."
-
--- The edited plugin is not an assistant plugin. Issue: {0}
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3984111892"] = "The edited plugin is not an assistant plugin. Issue: {0}"
-
--- The plugin system is not initialized yet.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3984839613"] = "The plugin system is not initialized yet."
-
--- The plugin file is outside the assistant plugin directory.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T4062980447"] = "The plugin file is outside the assistant plugin directory."
-
--- The edited assistant plugin is invalid. Issue: {0}
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T554567780"] = "The edited assistant plugin is invalid. Issue: {0}"
-
--- The edited assistant plugin must keep the same plugin ID.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T693124809"] = "The edited assistant plugin must keep the same plugin ID."
-
--- Internal assistant plugins cannot be edited.
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T816339833"] = "Internal assistant plugins cannot be edited."
-
--- The generated assistant plugin is invalid. Issue: {0}
-UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T939708112"] = "The generated assistant plugin is invalid. Issue: {0}"
-
-- The voice recording shortcut currently works only while AI Studio is focused.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T1204510649"] = "The voice recording shortcut currently works only while AI Studio is focused."
@@ -9475,6 +9814,144 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PANDOCAVAILABILITYSERVICE::T18544701
-- Pandoc may be required for importing files.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PANDOCAVAILABILITYSERVICE::T2596465560"] = "Pandoc may be required for importing files."
+-- This plugin archive declares itself as managed by a config server. Only the IT department of your organization might deploy such plugins.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1138181282"] = "This plugin archive declares itself as managed by a config server. Only the IT department of your organization might deploy such plugins."
+
+-- The imported plugin uses the ID of another installed plugin.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1195382910"] = "The imported plugin uses the ID of another installed plugin."
+
+-- The assistant plugin directory is outside the local assistant plugin directory.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1211881977"] = "The assistant plugin directory is outside the local assistant plugin directory."
+
+-- Only assistant plugins can be edited.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1288328479"] = "Only assistant plugins can be edited."
+
+-- The assistant cannot be deleted while background work is still running.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1318944584"] = "The assistant cannot be deleted while background work is still running."
+
+-- Plugins deployed by your organization cannot be deleted.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1348456011"] = "Plugins deployed by your organization cannot be deleted."
+
+-- The resolved plugin directory is outside the plugin directory.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1559620698"] = "The resolved plugin directory is outside the plugin directory."
+
+-- Please select a plugin archive with the extension .mwplugin or .zip.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1809137998"] = "Please select a plugin archive with the extension .mwplugin or .zip."
+
+-- The selected plugin archive does not exist.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1821013825"] = "The selected plugin archive does not exist."
+
+-- No Lua plugin code was generated.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1839013358"] = "No Lua plugin code was generated."
+
+-- Only assistant, configuration, and language plugins can be deleted.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T1878846406"] = "Only assistant, configuration, and language plugins can be deleted."
+
+-- Your organization has disabled importing configuration plugins.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2134532120"] = "Your organization has disabled importing configuration plugins."
+
+-- The assistant plugin directory does not exist.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2148384567"] = "The assistant plugin directory does not exist."
+
+-- The plugin directory does not exist.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2221093487"] = "The plugin directory does not exist."
+
+-- Unexpected error: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2350673880"] = "Unexpected error: {0}"
+
+-- The generated assistant plugin uses the ID of another installed plugin.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2441747251"] = "The generated assistant plugin uses the ID of another installed plugin."
+
+-- This individual plugin’s directory is outside the expected plugins directory.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2486199999"] = "This individual plugin’s directory is outside the expected plugins directory."
+
+-- The assistant plugin has no local directory.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2682912892"] = "The assistant plugin has no local directory."
+
+-- The AI Studio data directory is not initialized yet.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2712481762"] = "The AI Studio data directory is not initialized yet."
+
+-- Only assistant, configuration, and language plugins can be imported.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2909113247"] = "Only assistant, configuration, and language plugins can be imported."
+
+-- The generated plugin is not an assistant plugin. Issue: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T2955055168"] = "The generated plugin is not an assistant plugin. Issue: {0}"
+
+-- Your organization has disabled importing plugins.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T3212529834"] = "Your organization has disabled importing plugins."
+
+-- The plugin has no local directory.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T3284289028"] = "The plugin has no local directory."
+
+-- The plugin archive must contain exactly one plugin.lua file.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T3355918609"] = "The plugin archive must contain exactly one plugin.lua file."
+
+-- Your organization deployed a configuration with the same ID. An imported configuration must not take its place.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T352004699"] = "Your organization deployed a configuration with the same ID. An imported configuration must not take its place."
+
+-- The imported plugin is invalid. Issue: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T3634046009"] = "The imported plugin is invalid. Issue: {0}"
+
+-- Plugins shipped with AI Studio cannot be deleted.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T3841213017"] = "Plugins shipped with AI Studio cannot be deleted."
+
+-- The edited plugin is not an assistant plugin. Issue: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T3984111892"] = "The edited plugin is not an assistant plugin. Issue: {0}"
+
+-- The plugin system is not initialized yet.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T3984839613"] = "The plugin system is not initialized yet."
+
+-- The plugin file is outside the assistant plugin directory.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T4062980447"] = "The plugin file is outside the assistant plugin directory."
+
+-- Plugins deployed by your organization cannot be replaced.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T553820956"] = "Plugins deployed by your organization cannot be replaced."
+
+-- The edited assistant plugin is invalid. Issue: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T554567780"] = "The edited assistant plugin is invalid. Issue: {0}"
+
+-- The edited assistant plugin uses the ID of another installed plugin.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T584770023"] = "The edited assistant plugin uses the ID of another installed plugin."
+
+-- The edited assistant plugin must keep the same plugin ID.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T693124809"] = "The edited assistant plugin must keep the same plugin ID."
+
+-- Internal assistant plugins cannot be edited.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T816339833"] = "Internal assistant plugins cannot be edited."
+
+-- The generated assistant plugin is invalid. Issue: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGININSTALLSERVICE::T939708112"] = "The generated assistant plugin is invalid. Issue: {0}"
+
+-- Internal plugins cannot be shared.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T1668534561"] = "Internal plugins cannot be shared."
+
+-- Config Server managed plugins cannot be shared.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T2077776546"] = "Config Server managed plugins cannot be shared."
+
+-- The native share dialog could not be opened.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T2101116016"] = "The native share dialog could not be opened."
+
+-- The plugin directory does not exist.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T2221093487"] = "The plugin directory does not exist."
+
+-- Unexpected error: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T2350673880"] = "Unexpected error: {0}"
+
+-- The plugin has no local directory.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T3284289028"] = "The plugin has no local directory."
+
+-- Your organization has disabled sharing plugins.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T3379469503"] = "Your organization has disabled sharing plugins."
+
+-- The plugin directory is invalid: {0}
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T3774594541"] = "The plugin directory is invalid: {0}"
+
+-- Export plugin archive
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T3872669664"] = "Export plugin archive"
+
+-- The plugin directory does not contain a plugin.lua file.
+UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PLUGINSHARESERVICE::T409411078"] = "The plugin directory does not contain a plugin.lua file."
+
-- Failed to store the secret data due to an API issue.
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::RUSTSERVICE::T1110203516"] = "Failed to store the secret data due to an API issue."
diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor
index a369f6c1..cfcc28dc 100644
--- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor
+++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor
@@ -94,13 +94,13 @@
-
+
-
+
-
+
@if (this.selectedBriefing.Versions.Count == 0)
{
- @T("Create briefing")
+ @T("Create briefing")
}
else
{
diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Build.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Build.cs
index 5571f7c6..3f7a2a43 100644
--- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Build.cs
+++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.Build.cs
@@ -100,9 +100,12 @@ public partial class VisualBriefingAssistant
terminalStatus = result.FailureCode is VisualBriefingFailureCode.CANCELED ? AssistantSessionStatus.CANCELED : AssistantSessionStatus.FAILED;
this.reusableContentBuildId = result.CanContinueAsRebuild ? result.Diagnostics.BuildId : null;
- terminalIssue = result.Issue;
+ // The issue carried by the result is stable English contract language, because it also
+ // goes back to the model and into the persisted build record. What the user reads is
+ // derived from the stable enums in the current language instead:
+ terminalIssue = VisualBriefingFailureExtensions.ToUserMessage(result.FailureCode, result.Diagnostics.ValidationRule);
if (terminalStatus is not AssistantSessionStatus.CANCELED)
- await this.MessageBus.SendError(new(Icons.Material.Filled.AutoAwesome, result.Issue));
+ await this.MessageBus.SendError(new(Icons.Material.Filled.AutoAwesome, terminalIssue));
return;
}
diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs
index 5b478fcc..e9396100 100644
--- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs
+++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingAssistant.razor.cs
@@ -127,6 +127,9 @@ public partial class VisualBriefingAssistant : MSGComponentBase
/// Stores whether this component instance has already left the renderer.
private bool isDisposed;
+ /// Carries the spellchecking configuration to every text input of this assistant.
+ private static readonly Dictionary USER_INPUT_ATTRIBUTES = new();
+
///
/// Defines IsCurrentBusy for the visual briefing feature.
///
@@ -169,6 +172,16 @@ public partial class VisualBriefingAssistant : MSGComponentBase
await this.ResumeSelectedBuildAsync();
}
+ ///
+ /// Defines OnParametersSetAsync for the visual briefing feature.
+ ///
+ protected override async Task OnParametersSetAsync()
+ {
+ // Configure the spellchecking for the user input:
+ this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
+ await base.OnParametersSetAsync();
+ }
+
///
/// Defines DisposeResources for the visual briefing feature.
///
@@ -234,7 +247,12 @@ public partial class VisualBriefingAssistant : MSGComponentBase
}
if (triggeredEvent is Event.CONFIGURATION_CHANGED)
+ {
+ // The spellchecking setting might have changed. Since this page is not re-parameterized
+ // while the user stays on it, we have to read the setting again here:
+ this.SettingsManager.InjectSpellchecking(USER_INPUT_ATTRIBUTES);
this.StateHasChanged();
+ }
await base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
}
diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildProgress.razor.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildProgress.razor.cs
index cd8ee808..0480d41b 100644
--- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildProgress.razor.cs
+++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildProgress.razor.cs
@@ -279,9 +279,14 @@ public partial class VisualBriefingBuildProgress : MSGComponentBase
///
/// Gets the safe failure reason for a UI group.
///
+ ///
+ /// The recorded issue text of a failure is stable English contract language, because it also goes
+ /// back to the model and into the persisted build record. The text shown here is therefore derived
+ /// from the stable enums in the current language instead.
+ ///
/// The zero-based index of the group.
/// The user-facing failure message.
private string BuildGroupFailure(int index) => this.Build is null ? string.Empty : STAGE_GROUPS[index]
.Select(stage => this.Build.Stages.FirstOrDefault(item => item.Stage == stage)?.Failure)
- .FirstOrDefault(failure => failure is not null)?.UserMessage ?? this.Build.Failure?.UserMessage ?? string.Empty;
+ .FirstOrDefault(failure => failure is not null)?.ToUserMessage() ?? this.Build.Failure?.ToUserMessage() ?? string.Empty;
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildResult.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildResult.cs
index 0bea0811..56132e43 100644
--- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildResult.cs
+++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingBuildResult.cs
@@ -5,7 +5,7 @@ namespace AIStudio.Assistants.VisualBriefing;
///
/// Whether a revision was committed.
/// The committed immutable version.
-/// The user-safe issue.
+/// The user-safe issue in stable English, never localized. Use for the text shown to the user.
/// The stable failure code.
/// Safe technical diagnostics.
/// Whether incompatible valid content can continue without another content call.
diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingFailure.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingFailure.cs
index b67d11ad..1bab2a89 100644
--- a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingFailure.cs
+++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingFailure.cs
@@ -16,8 +16,14 @@ public sealed class VisualBriefingFailure
public VisualBriefingBuildStage Stage { get; set; }
///
- /// Gets or sets the localized or user-safe message.
+ /// Gets or sets the user-safe issue text in stable English.
///
+ ///
+ /// This text is never localized: it is sent back to the model as a repair instruction and it is
+ /// persisted with the build record, so both a translation and a later language switch would break
+ /// it. Use to
+ /// obtain the text shown to the user.
+ ///
public string UserMessage { get; set; } = string.Empty;
///
diff --git a/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingFailureExtensions.cs b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingFailureExtensions.cs
new file mode 100644
index 00000000..076d0dac
--- /dev/null
+++ b/app/MindWork AI Studio/Assistants/VisualBriefing/VisualBriefingFailureExtensions.cs
@@ -0,0 +1,108 @@
+namespace AIStudio.Assistants.VisualBriefing;
+
+///
+/// Translates the stable failure enums of one visual briefing operation into user-facing text.
+///
+///
+/// The issue texts that travel with a failure are contract language: they are sent back to the model
+/// as repair instructions, and they are persisted into the build record on disk. Both uses require
+/// stable English, so they can never be localized at their origin. The UI therefore keeps only the
+/// stable enums and asks for its text here, at render time, in the language selected right now.
+///
+internal static class VisualBriefingFailureExtensions
+{
+ private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(VisualBriefingFailureExtensions).Namespace, nameof(VisualBriefingFailureExtensions));
+
+ ///
+ /// Gets the localized message for one recorded failure.
+ ///
+ /// The recorded failure.
+ /// The localized message.
+ internal static string ToUserMessage(this VisualBriefingFailure failure) => ToUserMessage(failure.Code, failure.ValidationRule);
+
+ ///
+ /// Gets the localized message for one failure code and validation rule.
+ ///
+ ///
+ /// The failure code decides because it is the only value that is always about the failure at hand.
+ /// A validation rule is not: a failure records the rule of whichever stage recorded one, so a failed
+ /// commit or an incompatible content signature can carry the rule of an earlier stage. The two codes
+ /// below are the exception. They say no more than "the response was rejected", so there the rule
+ /// names the concrete violation and gives the better text.
+ ///
+ /// The stable failure code.
+ /// The stable validation rule.
+ /// The localized message.
+ internal static string ToUserMessage(VisualBriefingFailureCode code, VisualBriefingValidationRule rule) => code switch
+ {
+ VisualBriefingFailureCode.RESPONSE_JSON_INVALID or VisualBriefingFailureCode.RESPONSE_CONTRACT_INVALID when rule is not VisualBriefingValidationRule.NONE => rule.ToUserMessage(),
+
+ _ => code.ToUserMessage(),
+ };
+
+ ///
+ /// Gets the localized message for one validation rule.
+ ///
+ /// The stable validation rule.
+ /// The localized message.
+ private static string ToUserMessage(this VisualBriefingValidationRule rule) => rule switch
+ {
+ VisualBriefingValidationRule.JSON_INVALID => TB("The model did not return valid JSON. Please try again or select another model."),
+ VisualBriefingValidationRule.VALUE_TYPE_INVALID => TB("The model response contained a value of the wrong type. Please try again or select another model."),
+ VisualBriefingValidationRule.UNKNOWN_FIELD => TB("The model response contained unexpected fields. Please try again or select another model."),
+ VisualBriefingValidationRule.CONTRACT_VERSION_UNSUPPORTED => TB("The model response used an unsupported contract version. Please try again or select another model."),
+ VisualBriefingValidationRule.ID_INVALID => TB("The model response contained an empty, malformed, or duplicated identifier. Please try again or select another model."),
+ VisualBriefingValidationRule.REFERENCE_INVALID => TB("The model response referenced content that does not exist. Please try again or select another model."),
+ VisualBriefingValidationRule.SOURCE_COVERAGE_INVALID => TB("The model did not cover every source of this briefing exactly once. Please try again or select another model."),
+ VisualBriefingValidationRule.ASSET_PLAN_INVALID => TB("The model did not plan every visual asset of this briefing exactly once. Please try again or select another model."),
+ VisualBriefingValidationRule.SLOT_FULFILLMENT_INVALID => TB("The model did not fill every planned content slot exactly once. Please try again or select another model."),
+ VisualBriefingValidationRule.SLOT_VALUE_TYPE_INVALID => TB("The model filled a content slot with the wrong kind of value. Please try again or select another model."),
+ VisualBriefingValidationRule.CHART_SET_INVALID => TB("The charts of the model response did not match the planned briefing elements. Please try again or select another model."),
+ VisualBriefingValidationRule.CHART_DATA_INVALID => TB("A chart of the model response contained invalid categories or data series. Please try again or select another model."),
+ VisualBriefingValidationRule.CONTROL_ID_INVALID => TB("An interactive control of the model response used an invalid identifier. Please try again or select another model."),
+ VisualBriefingValidationRule.CONTROL_TARGET_INVALID => TB("An interactive control of the model response targeted an invalid briefing element. Please try again or select another model."),
+ VisualBriefingValidationRule.CONTROL_STATE_INVALID => TB("An interactive control of the model response used an invalid initial state. Please try again or select another model."),
+ VisualBriefingValidationRule.CONTROL_REQUIREMENT_INVALID => TB("A briefing element of the model response was missing its required interactive controls. Please try again or select another model."),
+ VisualBriefingValidationRule.FORMULA_TARGET_INVALID => TB("A calculation of the model response targeted an invalid briefing element. Please try again or select another model."),
+ VisualBriefingValidationRule.FORMULA_AST_INVALID => TB("A calculation of the model response used an invalid operation. Please try again or select another model."),
+ VisualBriefingValidationRule.ACCESSIBILITY_SET_INVALID => TB("The accessibility texts of the model response did not match the briefing elements. Please try again or select another model."),
+ VisualBriefingValidationRule.ACCESSIBILITY_TEXT_INVALID => TB("An accessibility text of the model response was empty or invalid. Please try again or select another model."),
+ VisualBriefingValidationRule.LAYOUT_INVALID => TB("The model response used an invalid briefing layout. Please try again or select another model."),
+ VisualBriefingValidationRule.TEMPLATE_ATTRIBUTE_PROHIBITED => TB("The model response used a prohibited attribute. Please try again or select another model."),
+ VisualBriefingValidationRule.MODEL_MARKUP_PROHIBITED => TB("The model response contained markup or code, which this briefing does not allow. Please try again or select another model."),
+ VisualBriefingValidationRule.COMPILER_OUTPUT_INVALID => TB("AI Studio compiled this briefing into an inconsistent result. Please copy the technical details and report this issue."),
+
+ _ => string.Empty,
+ };
+
+ ///
+ /// Gets the localized message for one failure code.
+ ///
+ /// The stable failure code.
+ /// The localized message.
+ private static string ToUserMessage(this VisualBriefingFailureCode code) => code switch
+ {
+ VisualBriefingFailureCode.PROVIDER_NOT_SELECTED => TB("This briefing has no provider selected. Please select a provider before you generate a briefing."),
+ VisualBriefingFailureCode.MODEL_CAPABILITY_MISSING => TB("The selected model lacks a capability this briefing needs. Please select another model."),
+ VisualBriefingFailureCode.SOURCE_UNREACHABLE => TB("A source of this briefing can no longer be reached. Please relink or remove the affected source."),
+ VisualBriefingFailureCode.TRANSCRIPT_UNAVAILABLE => TB("A media transcript of this briefing is missing or outdated. Please transcribe the affected media again."),
+ VisualBriefingFailureCode.SOURCE_PREPARATION_FAILED => TB("The sources of this briefing could not be prepared."),
+ VisualBriefingFailureCode.PROVIDER_CALL_FAILED => TB("The selected provider could not complete this briefing stage."),
+ VisualBriefingFailureCode.RESPONSE_JSON_INVALID => TB("The model did not return valid JSON. Please try again or select another model."),
+ VisualBriefingFailureCode.RESPONSE_CONTRACT_INVALID => TB("The model response did not match the required contract. Please try again or select another model."),
+ VisualBriefingFailureCode.COMPILER_INVARIANT_VIOLATED => TB("AI Studio compiled this briefing into an inconsistent result. Please copy the technical details and report this issue."),
+ VisualBriefingFailureCode.SOURCE_COVERAGE_INVALID => TB("The model did not cover every source of this briefing exactly once. Please try again or select another model."),
+ VisualBriefingFailureCode.ASSET_PLAN_INVALID => TB("The model did not plan every visual asset of this briefing exactly once. Please try again or select another model."),
+ VisualBriefingFailureCode.CONTENT_SIGNATURE_INCOMPATIBLE => TB("The updated content no longer fits the current presentation. You can continue as a rebuild."),
+ VisualBriefingFailureCode.PRESENTATION_INVALID => TB("The presentation of the model response did not match the briefing contract. Please try again or select another model."),
+ VisualBriefingFailureCode.ASSEMBLY_FAILED => TB("This briefing could not be assembled."),
+ VisualBriefingFailureCode.ARTIFACT_VALIDATION_FAILED => TB("The assembled briefing did not pass the security validation."),
+ VisualBriefingFailureCode.STORE_FAILED => TB("The new version of this briefing could not be saved."),
+ VisualBriefingFailureCode.NO_CHANGES => TB("This operation did not change the briefing, so no new version was created."),
+ VisualBriefingFailureCode.CANCELED => TB("This visual briefing operation was canceled."),
+ VisualBriefingFailureCode.BUILD_INTERRUPTED => TB("AI Studio was closed while this briefing was being built. You can resume the build."),
+ VisualBriefingFailureCode.UNEXPECTED => TB("This visual briefing operation failed because of an unexpected internal error. Please copy the technical details for support."),
+
+ _ => string.Empty,
+ };
+}
\ 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 ff639a0c..4486ae6c 100644
--- a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs
+++ b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs
@@ -9,7 +9,7 @@ using DialogOptions = AIStudio.Dialogs.DialogOptions;
namespace AIStudio.Components;
-public partial class AssistantBlock : MSGComponentBase where TSettings : IComponent
+public partial class AssistantBlock : MSGComponentBase, IAssistantCategoryMember where TSettings : IComponent
{
///
/// Describes the assistant session indicator shown on top of the assistant icon.
@@ -58,6 +58,12 @@ public partial class AssistantBlock : MSGComponentBase where TSetting
[Parameter]
public PreviewFeatures RequiredPreviewFeature { get; set; } = PreviewFeatures.NONE;
+ ///
+ /// Gets or sets the assistant category this block belongs to, if any.
+ ///
+ [CascadingParameter]
+ public AssistantCategoryBlock? Category { get; set; }
+
[Inject]
private MudTheme ColorTheme { get; init; } = null!;
@@ -88,7 +94,8 @@ public partial class AssistantBlock : MSGComponentBase where TSetting
private string BlockStyle => $"border-width: 3px; 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);
+ ///
+ public bool IsVisible => this.SettingsManager.IsAssistantVisible(this.Component, assistantName: this.Name, requiredPreviewFeature: this.RequiredPreviewFeature);
private bool HasSettingsPanel => typeof(TSettings) != typeof(NoSettingsPanel);
@@ -153,6 +160,7 @@ public partial class AssistantBlock : MSGComponentBase where TSetting
protected override async Task OnInitializedAsync()
{
this.MediaTranscriptionService.StateChanged += this.OnMediaImportStateChanged;
+ this.Category?.RegisterAssistant(this);
await base.OnInitializedAsync();
}
@@ -165,6 +173,7 @@ public partial class AssistantBlock : MSGComponentBase where TSetting
protected override void DisposeResources()
{
this.MediaTranscriptionService.StateChanged -= this.OnMediaImportStateChanged;
+ this.Category?.UnregisterAssistant(this);
base.DisposeResources();
}
diff --git a/app/MindWork AI Studio/Components/AssistantCategoryBlock.razor b/app/MindWork AI Studio/Components/AssistantCategoryBlock.razor
new file mode 100644
index 00000000..f6002b92
--- /dev/null
+++ b/app/MindWork AI Studio/Components/AssistantCategoryBlock.razor
@@ -0,0 +1,11 @@
+@if (this.HasVisibleAssistant)
+{
+
+ @this.Title
+
+}
+
+
+ @this.ChildContent
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/AssistantCategoryBlock.razor.cs b/app/MindWork AI Studio/Components/AssistantCategoryBlock.razor.cs
new file mode 100644
index 00000000..a204bb66
--- /dev/null
+++ b/app/MindWork AI Studio/Components/AssistantCategoryBlock.razor.cs
@@ -0,0 +1,70 @@
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Components;
+
+///
+/// Renders one category of assistants together with its heading.
+///
+///
+/// The heading is derived from the assistant blocks inside this category: it is rendered only when
+/// at least one of them is visible. Thus, hiding assistants by configuration can never leave an
+/// empty category heading behind.
+///
+public partial class AssistantCategoryBlock : ComponentBase
+{
+ private readonly HashSet members = [];
+
+ ///
+ /// The heading of this category.
+ ///
+ [Parameter]
+ public string Title { get; set; } = string.Empty;
+
+ ///
+ /// The CSS classes used for the heading.
+ ///
+ [Parameter]
+ public string HeaderClass { get; set; } = "mb-2 mr-3 mt-6";
+
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+
+ ///
+ /// Adds an assistant block to this category.
+ ///
+ ///
+ /// Assistant blocks call this while they initialize, i.e. after this category was rendered for
+ /// the first time. Hence, we have to render again to show the heading.
+ ///
+ /// The assistant block which belongs to this category.
+ internal void RegisterAssistant(IAssistantCategoryMember member)
+ {
+ if (this.members.Add(member))
+ this.StateHasChanged();
+ }
+
+ ///
+ /// Removes an assistant block from this category.
+ ///
+ /// The assistant block which no longer belongs to this category.
+ internal void UnregisterAssistant(IAssistantCategoryMember member) => this.members.Remove(member);
+
+ ///
+ /// Gets whether at least one assistant of this category is visible right now.
+ ///
+ ///
+ /// We evaluate this live instead of caching it. That way, changes to the configuration take
+ /// effect as soon as the assistants page renders again.
+ ///
+ private bool HasVisibleAssistant => this.members.Any(member => member.IsVisible);
+
+ ///
+ /// Gets the CSS classes used for the assistant stack.
+ ///
+ ///
+ /// The stack must be rendered even when no assistant is visible, because the assistant blocks
+ /// register themselves while rendering. Without any visible assistant, we drop the margin so
+ /// that a hidden category leaves no gap behind.
+ ///
+ private string StackClass => this.HasVisibleAssistant ? "mb-3" : string.Empty;
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor.cs b/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor.cs
deleted file mode 100644
index cd474c2c..00000000
--- a/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using AIStudio.Dialogs;
-using AIStudio.Tools.Media;
-using AIStudio.Tools.PluginSystem;
-using AIStudio.Tools.Services;
-using Microsoft.AspNetCore.Components;
-using DialogOptions = AIStudio.Dialogs.DialogOptions;
-
-namespace AIStudio.Components;
-
-public partial class AssistantPluginDeleteAction : MSGComponentBase
-{
- [Parameter, EditorRequired]
- public IAvailablePlugin Plugin { get; set; } = null!;
-
- [Inject]
- private IDialogService DialogService { get; init; } = null!;
-
- [Inject]
- private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!;
-
- [Inject]
- private MediaTranscriptionService MediaTranscriptionService { get; init; } = null!;
-
- [Inject]
- private ILogger Logger { get; init; } = null!;
-
- private bool CanDelete => AssistantPluginInstallService.CanDeleteInstalledAssistant(this.Plugin);
-
- private bool IsBlockedByActiveWork => this.AssistantPluginInstallService.HasActiveAssistantWork(this.Plugin.Id);
-
- private string Tooltip => this.IsBlockedByActiveWork
- ? this.T("The assistant cannot be deleted while background work is still running.")
- : this.T("Delete assistant plugin");
-
- protected override async Task OnInitializedAsync()
- {
- this.ApplyFilters([], [ Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED ]);
- this.MediaTranscriptionService.StateChanged += this.OnMediaTranscriptionStateChanged;
- await base.OnInitializedAsync();
- }
-
- private async Task DeleteAssistantPluginAsync()
- {
- if (!this.CanDelete || this.IsBlockedByActiveWork)
- return;
-
- var dialogParameters = new DialogParameters
- {
- {
- x => x.Message,
- string.Format(this.T("Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files."), this.Plugin.Name)
- },
- };
-
- var dialogReference = await this.DialogService.ShowAsync(this.T("Delete Assistant Plugin"), dialogParameters, DialogOptions.FULLSCREEN);
- var dialogResult = await dialogReference.Result;
- if (dialogResult is null || dialogResult.Canceled)
- return;
-
- var result = await this.AssistantPluginInstallService.DeleteInstalledAssistantAsync(this.Plugin, CancellationToken.None);
- if (!result.Success)
- {
- this.Logger.LogError("Failed to delete assistant plugin '{PluginName}' ({PluginId}) from '{PluginDirectory}' with issue '{Issue}'.", result.PluginName, result.PluginId, result.PluginDirectory, result.Issue);
- await this.MessageBus.SendError(new(Icons.Material.Filled.DeleteForever, string.Format(this.T("The assistant plugin '{0}' could not be deleted: {1}"), this.Plugin.Name, result.Issue)));
- return;
- }
-
- await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Check, string.Format(this.T("The '{0}' assistant plugin has been successfully removed."), result.PluginName)));
- }
-
- private void OnMediaTranscriptionStateChanged(MediaImportOwner owner)
- {
- if (owner.Kind is MediaImportOwnerKind.ASSISTANT && owner.Id.EndsWith($":{this.Plugin.Id}", StringComparison.Ordinal))
- _ = this.InvokeAsync(this.StateHasChanged);
- }
-
- protected override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
- {
- if (triggeredEvent is Event.ASSISTANT_SESSION_CHANGED or Event.ASSISTANT_SESSION_FINISHED)
- this.StateHasChanged();
-
- return base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
- }
-
- protected override void DisposeResources()
- {
- this.MediaTranscriptionService.StateChanged -= this.OnMediaTranscriptionStateChanged;
- base.DisposeResources();
- }
-}
diff --git a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs
index 9309a5b7..4b4274fd 100644
--- a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs
+++ b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs
@@ -222,6 +222,11 @@ public partial class AttachDocuments : MSGComponentBase
protected override void DisposeResources()
{
this.MediaTranscriptionService.StateChanged -= this.OnMediaImportStateChanged;
+
+ // Release the drop area. Without this, drop areas below this one would count this component
+ // forever and would stop catching dropped files:
+ _ = this.MessageBus.SendMessage(this, Event.UNREGISTER_FILE_DROP_AREA, this.Layer);
+
base.DisposeResources();
}
diff --git a/app/MindWork AI Studio/Components/ConfigurationBase.razor.cs b/app/MindWork AI Studio/Components/ConfigurationBase.razor.cs
index 33c896d1..20471d4d 100644
--- a/app/MindWork AI Studio/Components/ConfigurationBase.razor.cs
+++ b/app/MindWork AI Studio/Components/ConfigurationBase.razor.cs
@@ -56,7 +56,13 @@ public abstract partial class ConfigurationBase : MSGComponentBase
protected bool IsDisabled => this.Disabled() || this.IsLocked();
- private string Classes => $"{this.GetClassForBase} {JUSTIFIED_HELP_CLASS} {MARGIN_CLASS}";
+ private string Classes => $"{this.GetClassForBase} {JUSTIFIED_HELP_CLASS} {this.MarginClass}";
+
+ ///
+ /// The bottom margin of the option. Options inside settings panels need the default
+ /// spacing; standalone usages like toolbar buttons can remove it.
+ ///
+ protected virtual string MarginClass => MARGIN_CLASS;
private protected virtual RenderFragment? Body => null;
diff --git a/app/MindWork AI Studio/Components/IAssistantCategoryMember.cs b/app/MindWork AI Studio/Components/IAssistantCategoryMember.cs
new file mode 100644
index 00000000..f4dd3033
--- /dev/null
+++ b/app/MindWork AI Studio/Components/IAssistantCategoryMember.cs
@@ -0,0 +1,16 @@
+namespace AIStudio.Components;
+
+///
+/// Represents an assistant block which belongs to an assistant category.
+///
+///
+/// Assistant blocks are generic over their settings dialog. This interface gives the category block
+/// access to their visibility without the need to know that type parameter.
+///
+public interface IAssistantCategoryMember
+{
+ ///
+ /// Gets whether the assistant is visible right now.
+ ///
+ bool IsVisible { get; }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/LockableButton.razor b/app/MindWork AI Studio/Components/LockableButton.razor
index 825c5a62..6434a449 100644
--- a/app/MindWork AI Studio/Components/LockableButton.razor
+++ b/app/MindWork AI Studio/Components/LockableButton.razor
@@ -1,5 +1,8 @@
@inherits ConfigurationBaseCore
-
- @this.Text
-
\ No newline at end of file
+@* The tooltip is suppressed while the button is locked, so that the lock icon's tooltip is the only one shown: *@
+
+
+ @this.Text
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/LockableButton.razor.cs b/app/MindWork AI Studio/Components/LockableButton.razor.cs
index cbfbd910..ddec0bc1 100644
--- a/app/MindWork AI Studio/Components/LockableButton.razor.cs
+++ b/app/MindWork AI Studio/Components/LockableButton.razor.cs
@@ -18,7 +18,33 @@ public partial class LockableButton : ConfigurationBaseCore
[Parameter]
public string Class { get; set; } = string.Empty;
-
+
+ ///
+ /// An optional tooltip for the button. It is not shown while the button is locked,
+ /// because the lock icon explains the situation in that case.
+ ///
+ [Parameter]
+ public string Tooltip { get; set; } = string.Empty;
+
+ ///
+ /// The visual variant of the button.
+ ///
+ [Parameter]
+ public Variant ButtonVariant { get; set; } = Variant.Filled;
+
+ ///
+ /// The color of the button.
+ ///
+ [Parameter]
+ public Color ButtonColor { get; set; } = Color.Primary;
+
+ ///
+ /// Should the default bottom margin be removed? Useful when the button is placed in a
+ /// toolbar instead of a settings panel.
+ ///
+ [Parameter]
+ public bool NoMargin { get; set; }
+
#region Overrides of ConfigurationBase
///
@@ -26,6 +52,8 @@ public partial class LockableButton : ConfigurationBaseCore
protected override string GetClassForBase => this.Class;
+ protected override string MarginClass => this.NoMargin ? string.Empty : base.MarginClass;
+
#endregion
private async Task ClickAsync()
diff --git a/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor b/app/MindWork AI Studio/Components/PluginDeleteAction.razor
similarity index 67%
rename from app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor
rename to app/MindWork AI Studio/Components/PluginDeleteAction.razor
index 777b94d5..8001dcef 100644
--- a/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor
+++ b/app/MindWork AI Studio/Components/PluginDeleteAction.razor
@@ -7,7 +7,7 @@
Color="Color.Error"
Variant="Variant.Text"
Size="Size.Medium"
- Disabled="@this.IsBlockedByActiveWork"
- OnClick="@this.DeleteAssistantPluginAsync" />
+ Disabled="@(this.isDeleting || this.IsBlockedByActiveWork)"
+ OnClick="@this.DeletePluginAsync" />
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/PluginDeleteAction.razor.cs b/app/MindWork AI Studio/Components/PluginDeleteAction.razor.cs
new file mode 100644
index 00000000..e90ea1cf
--- /dev/null
+++ b/app/MindWork AI Studio/Components/PluginDeleteAction.razor.cs
@@ -0,0 +1,169 @@
+using AIStudio.Dialogs;
+using AIStudio.Tools.Media;
+using AIStudio.Tools.PluginSystem;
+using AIStudio.Tools.Services;
+
+using Microsoft.AspNetCore.Components;
+
+using DialogOptions = AIStudio.Dialogs.DialogOptions;
+
+namespace AIStudio.Components;
+
+///
+/// Lets users remove a plugin they installed or placed themselves.
+///
+///
+/// Without this action, such a plugin could only be removed from the data directory by hand. That is
+/// especially painful for configuration plugins, which have no activation switch at all. Plugins
+/// shipped with AI Studio and plugins deployed by an organization stay untouched: the action does
+/// not appear for them.
+///
+public partial class PluginDeleteAction : MSGComponentBase
+{
+ [Parameter, EditorRequired]
+ public IAvailablePlugin Plugin { get; set; } = null!;
+
+ [Inject]
+ private IDialogService DialogService { get; init; } = null!;
+
+ [Inject]
+ private PluginInstallService PluginInstallService { get; init; } = null!;
+
+ [Inject]
+ private MediaTranscriptionService MediaTranscriptionService { get; init; } = null!;
+
+ [Inject]
+ private ILogger Logger { get; init; } = null!;
+
+ private bool isDeleting;
+
+ private bool IsAssistant => this.Plugin.Type is PluginType.ASSISTANT;
+
+ private bool CanDelete => PluginInstallService.CanDeletePlugin(this.Plugin);
+
+ ///
+ /// True while an assistant still owns background work. We keep the action visible and block it
+ /// instead of hiding it, so that the tooltip can explain why it does nothing right now.
+ ///
+ private bool IsBlockedByActiveWork => this.IsAssistant && this.PluginInstallService.HasActiveAssistantWork(this.Plugin.Id);
+
+ private string Tooltip
+ {
+ get
+ {
+ if (this.IsBlockedByActiveWork)
+ return this.T("The assistant cannot be deleted while background work is still running.");
+
+ return this.Plugin.Type switch
+ {
+ PluginType.ASSISTANT => this.T("Delete assistant plugin"),
+ PluginType.CONFIGURATION => this.T("Delete configuration plugin"),
+
+ _ => this.T("Delete language plugin"),
+ };
+ }
+ }
+
+ #region Overrides of MSGComponentBase
+
+ protected override async Task OnInitializedAsync()
+ {
+ // Only an assistant can be busy. We watch its sessions and transcriptions, so the action
+ // reflects the current state without the user reloading the page:
+ this.ApplyFilters([], this.IsAssistant ? [Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED] : []);
+ if (this.IsAssistant)
+ this.MediaTranscriptionService.StateChanged += this.OnMediaTranscriptionStateChanged;
+
+ await base.OnInitializedAsync();
+ }
+
+ protected override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
+ {
+ if (triggeredEvent is Event.ASSISTANT_SESSION_CHANGED or Event.ASSISTANT_SESSION_FINISHED)
+ this.StateHasChanged();
+
+ return base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
+ }
+
+ protected override void DisposeResources()
+ {
+ if (this.IsAssistant)
+ this.MediaTranscriptionService.StateChanged -= this.OnMediaTranscriptionStateChanged;
+
+ base.DisposeResources();
+ }
+
+ #endregion
+
+ private async Task DeletePluginAsync()
+ {
+ if (!this.CanDelete || this.isDeleting || this.IsBlockedByActiveWork)
+ return;
+
+ if (!await this.ConfirmDeletionAsync())
+ return;
+
+ this.isDeleting = true;
+ await this.InvokeAsync(this.StateHasChanged);
+
+ try
+ {
+ var result = await this.PluginInstallService.DeletePluginAsync(this.Plugin, CancellationToken.None);
+ if (!result.Success)
+ {
+ this.Logger.LogError("Failed to delete {PluginType} plugin '{PluginName}' ({PluginId}) from '{PluginDirectory}' with issue '{Issue}'.", this.Plugin.Type, result.PluginName, result.PluginId, result.PluginDirectory, result.Issue);
+ await this.MessageBus.SendError(new(Icons.Material.Filled.DeleteForever, string.Format(this.T("The plugin '{0}' could not be deleted: {1}"), this.Plugin.Name, result.Issue)));
+ return;
+ }
+
+ await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Check, string.Format(this.T("The plugin '{0}' has been successfully removed."), result.PluginName)));
+ }
+ finally
+ {
+ this.isDeleting = false;
+ await this.InvokeAsync(this.StateHasChanged);
+ }
+ }
+
+ ///
+ /// Asks the user before the deletion. A configuration gets the dialog listing its consequences,
+ /// because removing it also removes the providers and settings it brought. Assistants and
+ /// language plugins only own their own files, so a plain confirmation is enough.
+ ///
+ private async Task ConfirmDeletionAsync()
+ {
+ if (this.Plugin.Type is PluginType.CONFIGURATION)
+ {
+ var configurationParameters = new DialogParameters
+ {
+ { x => x.PluginName, this.Plugin.Name },
+ { x => x.Summary, this.PluginInstallService.BuildConfigurationDeleteSummary(this.Plugin) },
+ };
+
+ var configurationDialog = await this.DialogService.ShowAsync(this.T("Delete Configuration Plugin"), configurationParameters, DialogOptions.FULLSCREEN);
+ return await configurationDialog.Result is { Canceled: false };
+ }
+
+ var title = this.IsAssistant
+ ? this.T("Delete Assistant Plugin")
+ : this.T("Delete Language Plugin");
+
+ var message = this.IsAssistant
+ ? string.Format(this.T("Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files."), this.Plugin.Name)
+ : string.Format(this.T("Do you really want to delete the language plugin '{0}'? This permanently deletes its local plugin files. When it is your chosen language, AI Studio returns to choosing the language automatically."), this.Plugin.Name);
+
+ var parameters = new DialogParameters
+ {
+ { x => x.Message, message },
+ };
+
+ var dialog = await this.DialogService.ShowAsync(title, parameters, DialogOptions.FULLSCREEN);
+ return await dialog.Result is { Canceled: false };
+ }
+
+ private void OnMediaTranscriptionStateChanged(MediaImportOwner owner)
+ {
+ if (owner.Kind is MediaImportOwnerKind.ASSISTANT && owner.Id.EndsWith($":{this.Plugin.Id}", StringComparison.Ordinal))
+ _ = this.InvokeAsync(this.StateHasChanged);
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs
index 049e5b35..cf23d97c 100644
--- a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs
+++ b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs
@@ -174,10 +174,16 @@ public partial class ReadFileContent : MSGComponentBase
this.MediaTranscriptionService.AcknowledgeDelivery(delivery);
}
- /// Unsubscribes from the singleton media service.
+ /// Unsubscribes from the singleton media service and releases the drop area.
protected override void DisposeResources()
{
this.MediaTranscriptionService.StateChanged -= this.OnMediaImportStateChanged;
+
+ // Release the drop area. Without this, drop areas below this one would count this component
+ // forever and would stop catching dropped files:
+ if (this.EnableDragDrop)
+ _ = this.MessageBus.SendMessage(this, Event.UNREGISTER_FILE_DROP_AREA, this.Layer);
+
base.DisposeResources();
}
diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs b/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs
index 3f43d8a3..a05a4e98 100644
--- a/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs
+++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelApp.razor.cs
@@ -108,8 +108,10 @@ public partial class SettingsPanelApp : SettingsPanelBase
private HashSet GetPluginContributedPreviewFeatures()
{
+ // Several configuration plugins may contribute at the same time, e.g. one preview feature
+ // for the whole organization and another one for a single department:
if (ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.HasPluginContribution)
- return meta.PluginContribution.Where(x => !x.IsReleased()).ToHashSet();
+ return meta.PluginContributions.Values.SelectMany(contribution => contribution).Where(x => !x.IsReleased()).ToHashSet();
return [];
}
@@ -122,7 +124,7 @@ public partial class SettingsPanelApp : SettingsPanelBase
if (!ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) || !meta.HasPluginContribution)
return false;
- return meta.PluginContribution.Contains(feature);
+ return meta.PluginContributions.Values.Any(contribution => contribution.Contains(feature));
}
private HashSet GetSelectedPreviewFeatures()
diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelEmbeddings.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelEmbeddings.razor
index dc713dda..f89c07d0 100644
--- a/app/MindWork AI Studio/Components/Settings/SettingsPanelEmbeddings.razor
+++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelEmbeddings.razor
@@ -40,9 +40,9 @@
- @if (context.IsTrustedByConfiguration(this.SettingsManager))
+ @if (context.IsTrustedForDataSourceSecurityChecks(this.SettingsManager))
{
-
+
}
diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor
index 4f954b5f..5ec93e3e 100644
--- a/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor
+++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelProviders.razor
@@ -31,9 +31,9 @@
@this.GetLLMProviderModelName(context)
- @if (context.IsTrustedByConfiguration(this.SettingsManager))
+ @if (context.IsTrustedForDataSourceSecurityChecks(this.SettingsManager))
{
-
+
}
diff --git a/app/MindWork AI Studio/Components/Settings/SettingsPanelTranscription.razor b/app/MindWork AI Studio/Components/Settings/SettingsPanelTranscription.razor
index fbbd009e..f0a9c6f2 100644
--- a/app/MindWork AI Studio/Components/Settings/SettingsPanelTranscription.razor
+++ b/app/MindWork AI Studio/Components/Settings/SettingsPanelTranscription.razor
@@ -36,9 +36,9 @@
- @if (context.IsTrustedByConfiguration(this.SettingsManager))
+ @if (context.IsTrustedForDataSourceSecurityChecks(this.SettingsManager))
{
-
+
}
diff --git a/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor b/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor
index 53facb3d..bb39b568 100644
--- a/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor
+++ b/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor
@@ -3,13 +3,6 @@
- @if (!string.IsNullOrWhiteSpace(this.issue))
- {
-
- @this.issue
-
- }
-
@if (this.isLoading)
{
@@ -35,6 +28,12 @@
+ @if (!string.IsNullOrWhiteSpace(this.issue))
+ {
+
+ @this.issue
+
+ }
@T("Cancel")
diff --git a/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor.cs b/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor.cs
index c759e5ac..52a9a329 100644
--- a/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor.cs
+++ b/app/MindWork AI Studio/Dialogs/AssistantPluginEditorDialog.razor.cs
@@ -29,7 +29,7 @@ public partial class AssistantPluginEditorDialog : MSGComponentBase
private IMudDialogInstance MudDialog { get; set; } = null!;
[Inject]
- private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!;
+ private PluginInstallService PluginInstallService { get; init; } = null!;
[Parameter]
public Guid PluginId { get; set; }
@@ -105,7 +105,7 @@ public partial class AssistantPluginEditorDialog : MSGComponentBase
try
{
var editedLua = await this.codeEditor.GetCodeAsync();
- var result = await this.AssistantPluginInstallService.UpdateInstalledAssistantAsync(this.plugin, editedLua, CancellationToken.None);
+ var result = await this.PluginInstallService.UpdateInstalledAssistantAsync(this.plugin, editedLua, CancellationToken.None);
if (!result.Success)
{
LOGGER.LogError($"Failed to update assistant plugin '{result.PluginName}' ({result.PluginId}) in '{result.PluginDirectory}' with issue '{result.Issue}'.");
diff --git a/app/MindWork AI Studio/Dialogs/AssistantPluginRevisionDialog.razor.cs b/app/MindWork AI Studio/Dialogs/AssistantPluginRevisionDialog.razor.cs
index cd136008..b579e8ea 100644
--- a/app/MindWork AI Studio/Dialogs/AssistantPluginRevisionDialog.razor.cs
+++ b/app/MindWork AI Studio/Dialogs/AssistantPluginRevisionDialog.razor.cs
@@ -23,7 +23,7 @@ public partial class AssistantPluginRevisionDialog : MSGComponentBase
private AssistantPluginGenerationService AssistantPluginGenerationService { get; init; } = null!;
[Inject]
- private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!;
+ private PluginInstallService PluginInstallService { get; init; } = null!;
[Inject]
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
@@ -144,7 +144,7 @@ public partial class AssistantPluginRevisionDialog : MSGComponentBase
if (this.availablePlugin is null)
return;
- this.revisionCheckResult = await this.AssistantPluginInstallService.CheckInstalledAssistantUpdateAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
+ this.revisionCheckResult = await this.PluginInstallService.CheckInstalledAssistantUpdateAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
if (this.revisionCheckResult.Success)
return;
@@ -168,7 +168,7 @@ public partial class AssistantPluginRevisionDialog : MSGComponentBase
try
{
- var result = await this.AssistantPluginInstallService.UpdateInstalledAssistantAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
+ var result = await this.PluginInstallService.UpdateInstalledAssistantAsync(this.availablePlugin, this.revisedLua, CancellationToken.None);
if (!result.Success)
{
LOGGER.LogError($"Failed to revise assistant plugin '{result.PluginName}' ({result.PluginId}) in '{result.PluginDirectory}' with issue '{result.Issue}'.");
diff --git a/app/MindWork AI Studio/Dialogs/ConfigurationPluginDeleteDialog.razor b/app/MindWork AI Studio/Dialogs/ConfigurationPluginDeleteDialog.razor
new file mode 100644
index 00000000..abe31499
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/ConfigurationPluginDeleteDialog.razor
@@ -0,0 +1,42 @@
+@inherits MSGComponentBase
+
+
+
+ @(string.Format(T("Do you really want to delete the configuration plugin '{0}'? This permanently deletes its local plugin files."), this.PluginName))
+
+
+ @if (this.Consequences.Count > 0)
+ {
+
+ @T("This also removes everything the configuration plugin had set up:")
+
+
+
+ @foreach (var consequence in this.Consequences)
+ {
+
+ @consequence
+
+ }
+
+ }
+ else
+ {
+
+ @T("The configuration plugin is not running, so we cannot tell what it had set up. Anything it configured will be removed as well.")
+
+ }
+
+
+ @T("You can install the plugin again later, but any changes you made to its settings are lost.")
+
+
+
+
+ @T("No")
+
+
+ @T("Yes, delete it")
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/ConfigurationPluginDeleteDialog.razor.cs b/app/MindWork AI Studio/Dialogs/ConfigurationPluginDeleteDialog.razor.cs
new file mode 100644
index 00000000..314b3bae
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/ConfigurationPluginDeleteDialog.razor.cs
@@ -0,0 +1,69 @@
+using AIStudio.Components;
+using AIStudio.Tools.Services;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Dialogs;
+
+///
+/// Asks the user whether a local configuration plugin may be deleted, and shows what the deletion
+/// takes with it.
+///
+public partial class ConfigurationPluginDeleteDialog : MSGComponentBase
+{
+ [CascadingParameter]
+ private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ ///
+ /// The name of the configuration plugin about to be deleted.
+ ///
+ [Parameter]
+ public string PluginName { get; set; } = string.Empty;
+
+ ///
+ /// What the deletion removes besides the plugin directory.
+ ///
+ [Parameter]
+ public ConfigurationPluginDeleteSummary Summary { get; set; } = ConfigurationPluginDeleteSummary.EMPTY;
+
+ private List Consequences => this.BuildConsequences();
+
+ ///
+ /// Turns the summary into the lines shown to the user. Only what is actually affected is listed,
+ /// so the dialog stays short for a configuration plugin that just locks a single setting.
+ ///
+ private List BuildConsequences()
+ {
+ var consequences = new List();
+ var summary = this.Summary;
+
+ Add(summary.LlmProviders, this.T("{0} LLM provider"), this.T("{0} LLM providers"));
+ Add(summary.TranscriptionProviders, this.T("{0} transcription provider"), this.T("{0} transcription providers"));
+ Add(summary.EmbeddingProviders, this.T("{0} embedding provider"), this.T("{0} embedding providers"));
+ Add(summary.ChatTemplates, this.T("{0} chat template"), this.T("{0} chat templates"));
+ Add(summary.Profiles, this.T("{0} profile"), this.T("{0} profiles"));
+ Add(summary.DocumentAnalysisPolicies, this.T("{0} document analysis policy"), this.T("{0} document analysis policies"));
+ Add(summary.MandatoryInfos, this.T("{0} mandatory information"), this.T("{0} mandatory informations"));
+ Add(summary.Introductions, this.T("{0} introduction on the welcome page"), this.T("{0} introductions on the welcome page"));
+
+ // Data sources are called out separately: removing them also deletes their credentials from
+ // the operating system's keychain, which the user cannot undo by reinstalling the plugin.
+ Add(summary.DataSources,
+ this.T("{0} data source, including its credentials in your operating system's keychain"),
+ this.T("{0} data sources, including their credentials in your operating system's keychain"));
+
+ Add(summary.LockedSettings, this.T("{0} setting returns to its default value"), this.T("{0} settings return to their default values"));
+
+ return consequences;
+
+ void Add(int count, string singular, string plural)
+ {
+ if (count > 0)
+ consequences.Add(string.Format(count == 1 ? singular : plural, count));
+ }
+ }
+
+ private void Cancel() => this.MudDialog.Cancel();
+
+ private void Confirm() => this.MudDialog.Close(DialogResult.Ok(true));
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/InformationDialog.razor b/app/MindWork AI Studio/Dialogs/InformationDialog.razor
new file mode 100644
index 00000000..02128ffd
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/InformationDialog.razor
@@ -0,0 +1,16 @@
+@inherits MSGComponentBase
+
+
+
+
+
+ @this.Message
+
+
+
+
+
+ @T("Close")
+
+
+
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/InformationDialog.razor.cs b/app/MindWork AI Studio/Dialogs/InformationDialog.razor.cs
new file mode 100644
index 00000000..3d58e676
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/InformationDialog.razor.cs
@@ -0,0 +1,35 @@
+using AIStudio.Components;
+
+using Microsoft.AspNetCore.Components;
+
+namespace AIStudio.Dialogs;
+
+///
+/// A dialog that informs the user about something without asking for a decision. Use it when a
+/// message must not be missed, e.g., when an action was refused.
+///
+public partial class InformationDialog : MSGComponentBase
+{
+ [CascadingParameter]
+ private IMudDialogInstance MudDialog { get; set; } = null!;
+
+ ///
+ /// The message shown to the user.
+ ///
+ [Parameter]
+ public string Message { get; set; } = string.Empty;
+
+ ///
+ /// The icon shown next to the message.
+ ///
+ [Parameter]
+ public string Icon { get; set; } = Icons.Material.Filled.Info;
+
+ ///
+ /// The color of the icon.
+ ///
+ [Parameter]
+ public Color IconColor { get; set; } = Color.Info;
+
+ private void Close() => this.MudDialog.Close(DialogResult.Ok(true));
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Dialogs/PluginImportDialog.razor b/app/MindWork AI Studio/Dialogs/PluginImportDialog.razor
new file mode 100644
index 00000000..3bb882cc
--- /dev/null
+++ b/app/MindWork AI Studio/Dialogs/PluginImportDialog.razor
@@ -0,0 +1,105 @@
+@inherits MSGComponentBase
+
+
+
+ @this.IntroductionText @T("Plugins contain code that runs inside AI Studio. Install plugins only when you trust their source.")
+
+
+
+
+ @this.Preview.Plugin.Name
+
+
+ @this.Preview.Plugin.Description
+
+
+ @T("Type"): @this.TypeLabel
+
+
+ @T("Version"): @this.Preview.Plugin.Version
+
+
+ @T("Authors"): @this.AuthorsLabel
+
+ @if (!string.IsNullOrWhiteSpace(this.Preview.Plugin.SourceURL))
+ {
+
+ @T("Source"): @this.Preview.Plugin.SourceURL
+
+ }
+ @if (!string.IsNullOrWhiteSpace(this.Preview.Plugin.SupportContact))
+ {
+
+ @T("Support contact"): @this.Preview.Plugin.SupportContact
+
+ }
+
+
+ @if (this.Preview.ConfigurationSummary is { HasAnyContent: true } configurationSummary)
+ {
+
+ @T("A configuration takes effect right after the installation and has no on/off switch. Please check what it sets up:")
+
+
+ @if (configurationSummary.Destinations.Count > 0)
+ {
+
+
+