Added the choice between formal and informal address to the whole app (#1200)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-11-13 10:16:56 +01:00
1 parent 382db4b8d0
commit c9a376f742
342 files changed
+14378 -1893

No files matched your search

@@ -141,9 +141,9 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase, IAssistantCat
{
MediaImportStatus.QUEUED or MediaImportStatus.RUNNING or MediaImportStatus.CANCELING => new(AppIcons.RUNNING, Color.Info, this.T("Media is still being prepared.")),
MediaImportStatus.SUCCEEDED => new(AppIcons.SUCCESS, Color.Success, this.T("The media transcript is ready.")),
MediaImportStatus.WARNING => new(AppIcons.WARNING, Color.Warning, this.T("Media transcription completed with a warning. Open the assistant to review it.")),
MediaImportStatus.FAILED => new(AppIcons.ERROR, Color.Error, this.T("Media transcription failed. Open the assistant to review it.")),
MediaImportStatus.CANCELLED => new(AppIcons.CANCELED, Color.Warning, this.T("Media transcription was canceled. Open the assistant to review it.")),
MediaImportStatus.WARNING => new(AppIcons.WARNING, Color.Warning, this.TA("Media transcription completed with a warning. Open the assistant to review it.")),
MediaImportStatus.FAILED => new(AppIcons.ERROR, Color.Error, this.TA("Media transcription failed. Open the assistant to review it.")),
MediaImportStatus.CANCELLED => new(AppIcons.CANCELED, Color.Warning, this.TA("Media transcription was canceled. Open the assistant to review it.")),
_ => this.AssistantSessionIndicatorWithoutMedia,
};
@@ -152,8 +152,8 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase, IAssistantCat
{
AssistantSessionStatus.RUNNING or AssistantSessionStatus.CANCELING => new(AppIcons.RUNNING, Color.Info, this.T("Assistant is still running.")),
AssistantSessionStatus.COMPLETED => new(AppIcons.SUCCESS, Color.Success, this.T("The result is ready.")),
AssistantSessionStatus.FAILED => new(AppIcons.ERROR, Color.Error, this.T("Assistant failed. Open it to review the result.")),
AssistantSessionStatus.CANCELED => new(AppIcons.CANCELED, Color.Warning, this.T("Assistant was canceled. Open it to review the result.")),
AssistantSessionStatus.FAILED => new(AppIcons.ERROR, Color.Error, this.TA("Assistant failed. Open it to review the result.")),
AssistantSessionStatus.CANCELED => new(AppIcons.CANCELED, Color.Warning, this.TA("Assistant was canceled. Open it to review the result.")),
_ => null,
};
@@ -80,7 +80,7 @@
<MudDivider Vertical="@true" FlexItem="@true" />
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@AppIcons.LOCK" Size="Size.Small" Color="Color.Success" />
<MudText Typo="Typo.body2">@T("Your organization requires this assistant to stay enabled")</MudText>
<MudText Typo="Typo.body2">@TA("Your organization requires this assistant to stay enabled")</MudText>
</MudStack>
}
}
@@ -212,8 +212,8 @@
<td>
<MudText Typo="Typo.body2">
@(state.IsActivationEnforcedByOrganization
? T("Required by your organization")
: T("Enabled by your organization, you may switch it off"))
? TA("Required by your organization")
: TA("Enabled by your organization, you may switch it off"))
</MudText>
</td>
</tr>
@@ -268,7 +268,7 @@
@if (state.IsEnterpriseApproved)
{
<MudAlert Severity="Severity.Success" Variant="Variant.Text" Dense="@true">
@T("This plugin is approved by your organization. A manual security audit is not required.")
@TA("This plugin is approved by your organization. A manual security audit is not required.")
</MudAlert>
}
else
@@ -32,7 +32,7 @@
}
else if (this.DocumentPaths.Any())
{
<MudTooltip Text="@T("Click the paperclip to attach files, or click the number to see your attached files.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudTooltip Text="@TA("Click the paperclip to attach files, or click the number to see your attached files.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudBadge
Content="@this.DocumentPaths.Count"
Color="Color.Primary"
@@ -49,7 +49,7 @@
}
else
{
<MudTooltip Text="@T("Click here to attach files.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudTooltip Text="@TA("Click here to attach files.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudIconButton
Icon="@AppIcons.ATTACH_FILE"
Color="Color.Default"
@@ -70,7 +70,7 @@ else
{
<MudStack Row="true" AlignItems="AlignItems.Center" StretchItems="StretchItems.None" Wrap="Wrap.Wrap">
<MudText Typo="Typo.body1" Inline="true">
@T("Drag and drop files into the marked area or click here to attach documents: ")
@TA("Drag and drop files into the marked area or click here to attach documents: ")
</MudText>
<MudButton
Variant="Variant.Filled"
@@ -367,7 +367,7 @@ public partial class AttachDocuments : MSGComponentBase
this.Logger.LogWarning("Could not access {Count} dropped or selected file(s): {Paths}", inaccessiblePaths.Count, string.Join(", ", inaccessiblePaths));
await this.MessageBus.SendWarning(new(
AppIcons.WARNING,
this.T("Some files could not be accessed. Please select them with the file chooser instead.")));
this.TA("Some files could not be accessed. Please select them with the file chooser instead.")));
}
var existingPaths = pathList.Except(inaccessiblePaths).ToList();
@@ -422,7 +422,7 @@ public partial class AttachDocuments : MSGComponentBase
{
await this.MessageBus.SendWarning(new(
AppIcons.VOICE_TRANSCRIPTION,
this.T("Media files require a configured transcription provider. Configure one in the transcription settings.")));
this.TA("Media files require a configured transcription provider. Configure one in the transcription settings.")));
return attached;
}
@@ -68,7 +68,7 @@
this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES
&& this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_OVERLAY)
{
<MudTooltip Text="@T("Show your workspaces")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudTooltip Text="@TA("Show your workspaces")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudIconButton Icon="@AppIcons.WORKSPACES" OnClick="@(() => this.ToggleWorkspaceOverlay())"/>
</MudTooltip>
}
@@ -103,7 +103,7 @@
@if (this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES)
{
<MudTooltip Text="@T("Move the chat to a workspace, or to another if it is already in one.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudTooltip Text="@TA("Move the chat to a workspace, or to another if it is already in one.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudIconButton Icon="@AppIcons.MOVE_TO_WORKSPACE" Disabled="@(!this.CanThreadBeMoved)" OnClick="@this.MoveChatToWorkspace"/>
</MudTooltip>
@@ -151,7 +151,7 @@
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
{
<DataSourceSelection @ref="@this.dataSourceSelectionComponent" LLMProvider="@this.Provider" DataSourceOptions="@this.GetCurrentDataSourceOptions()" DataSourceOptionsChanged="@this.DataSourceOptionsWereChanged" DataSourcesAISelected="@this.GetAgentSelectedDataSources()" ReadOnly="@this.IsCurrentChatStreaming" ReadOnlyMessage="@T("Data source changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished.")"/>
<DataSourceSelection @ref="@this.dataSourceSelectionComponent" LLMProvider="@this.Provider" DataSourceOptions="@this.GetCurrentDataSourceOptions()" DataSourceOptionsChanged="@this.DataSourceOptionsWereChanged" DataSourcesAISelected="@this.GetAgentSelectedDataSources()" ReadOnly="@this.IsCurrentChatStreaming" ReadOnlyMessage="@TA("Data source changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished.")"/>
}
@if (this.SettingsManager.ConfigurationData.Confidence.ShowProviderConfidence)
@@ -168,7 +168,7 @@
@if (!this.ChatThread.IsLLMProviderAllowed(this.Provider))
{
<MudTooltip Text="@(this.SettingsManager.IsProviderProhibited(this.Provider.UsedLLMProvider) ? T("The use of this provider has been prohibited by your organization. Please select a different provider.") : T("The selected provider is not allowed in this chat due to data security or confidence-level requirements."))" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudTooltip Text="@(this.SettingsManager.IsProviderProhibited(this.Provider.UsedLLMProvider) ? TA("The use of this provider has been prohibited by your organization. Please select a different provider.") : T("The selected provider is not allowed in this chat due to data security or confidence-level requirements."))" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
<MudIconButton Icon="@AppIcons.WARNING" Color="Color.Error"/>
</MudTooltip>
}
@@ -257,7 +257,7 @@ public partial class ChatComponent : MSGComponentBase
budget = string.Format(this.T("{0}, of which approx. {1} from tools"), budget, TokenAmount.Format(this.conversationTokens.ToolTokens, this.currentCulture));
if (this.conversationTokens.DraftTokens > 0)
budget = string.Format(this.T("{0}, plus approx. {1} for your message"), budget, TokenAmount.Format(this.conversationTokens.DraftTokens, this.currentCulture));
budget = string.Format(this.TA("{0}, plus approx. {1} for your message"), budget, TokenAmount.Format(this.conversationTokens.DraftTokens, this.currentCulture));
//
// The pictures of the whole conversation, not of the message being written: every one
@@ -789,16 +789,16 @@ public partial class ChatComponent : MSGComponentBase
private bool IsCurrentChatStreaming => this.ChatThread is not null && this.AIJobService.IsChatGenerationActive(this.ChatThread.ChatId);
private string ProviderPlaceholder => this.IsProviderSelected ? T("Type your input here...") : T("Select a provider first");
private string ProviderPlaceholder => this.IsProviderSelected ? TA("Type your input here...") : TA("Select a provider first");
private string InputLabel
{
get
{
if (this.IsProviderSelected)
return string.Format(T("Your Prompt (use selected instance '{0}', provider '{1}')"), this.Provider.InstanceName, this.Provider.UsedLLMProvider.Name);
return string.Format(TA("Your Prompt (use selected instance '{0}', provider '{1}')"), this.Provider.InstanceName, this.Provider.UsedLLMProvider.Name);
return this.T("Select a provider first");
return this.TA("Select a provider first");
}
}
@@ -833,7 +833,7 @@ public partial class ChatComponent : MSGComponentBase
// The chat template locks the profile for good, an answer being written only until it is complete:
private string ProfileDisabledText => this.currentChatTemplate.AllowProfileUsage
? T("Profile changes are locked while a response is running.")
: T("Profile usage is disabled according to your chat template settings.");
: TA("Profile usage is disabled according to your chat template settings.");
/// <summary>
/// Whether the open chat can move into a workspace, which is exactly when the workspace tree offers it.
@@ -1927,7 +1927,7 @@ public partial class ChatComponent : MSGComponentBase
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, T("Are you sure you want to start a new chat? All unsaved changes will be lost.") },
{ x => x.Message, TA("Are you sure you want to start a new chat? All unsaved changes will be lost.") },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Start New Chat"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -2084,7 +2084,7 @@ public partial class ChatComponent : MSGComponentBase
var dialogParameters = new DialogParameters<WorkspaceSelectionDialog>
{
{ x => x.Message, T("Please select the workspace where you want to move the chat to.") },
{ x => x.Message, TA("Please select the workspace where you want to move the chat to.") },
{ x => x.SelectedWorkspace, this.ChatThread?.WorkspaceId ?? Guid.Empty },
{ x => x.ConfirmText, T("Move chat") },
};
@@ -16,7 +16,7 @@
}
</ActivatorContent>
<ChildContent>
<MudMenuItem Icon="@AppIcons.SETTINGS" Label="@T("Manage your templates")" OnClick="@(async () => await this.OpenSettingsDialog())" />
<MudMenuItem Icon="@AppIcons.SETTINGS" Label="@TA("Manage your templates")" OnClick="@(async () => await this.OpenSettingsDialog())" />
<MudDivider/>
<MudMenuItem Icon="@AppIcons.CREATE_CHAT_TEMPLATE" Label="@T("Create template from current chat")" OnClick="@(async () => await this.CreateNewChatTemplateFromChat())" Disabled="@(!this.CanChatThreadBeUsedForTemplate)"/>
<MudDivider/>
@@ -7,7 +7,7 @@
<MudField Label="@this.Label" Variant="@this.Variant" Underline="false" HelperText="@this.OptionHelp" Class="@this.Classes" InnerPadding="false">
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.FlexStart" Wrap="Wrap.NoWrap" StretchItems="this.StretchItems">
@* MudTooltip.RootStyle is set as a workaround for issue -> https://github.com/MudBlazor/MudBlazor/issues/10882 *@
<MudTooltip Text="@TB("This feature is managed by your organization and has therefore been disabled.")" Arrow="true" Placement="Placement.Right" RootStyle="display:inline-flex;">
<MudTooltip Text="@TA("This feature is managed by your organization and has therefore been disabled.")" Arrow="true" Placement="Placement.Right" RootStyle="display:inline-flex;">
<MudIcon Icon="@AppIcons.LOCK" Color="Color.Error" Size="Size.Small" Class="mr-1"/>
</MudTooltip>
@this.Body
@@ -83,7 +83,11 @@ public abstract partial class ConfigurationBase : MSGComponentBase
#endregion
private string TB(string fallbackEN) => this.T(fallbackEN, typeof(ConfigurationBase).Namespace, nameof(ConfigurationBase));
/// <summary>
/// Gets a text of this base class which addresses the reader; the collector files it under ConfigurationBase,
/// not under the component which derives from it.
/// </summary>
private new string TA(string fallbackEN) => this.TA(fallbackEN, typeof(ConfigurationBase).Namespace, nameof(ConfigurationBase));
protected async Task InformAboutChange() => await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
@@ -7,7 +7,7 @@
Text="@this.Text()"
TextChanged="@this.InternalUpdate"
Disabled="@this.IsDisabled"
Placeholder="@(this.IsDisabled ? string.Empty : T("You can also drag & drop the folder here."))"
Placeholder="@(this.IsDisabled ? string.Empty : TA("You can also drag & drop the folder here."))"
Adornment="Adornment.Start"
AdornmentIcon="@this.Icon"
AdornmentColor="@(isDropTarget ? Color.Primary : this.IconColor)"
@@ -7,7 +7,7 @@
Text="@this.Text()"
TextChanged="@this.InternalUpdate"
Disabled="@this.IsDisabled"
Placeholder="@(this.IsDisabled ? string.Empty : T("You can also drag & drop the file here."))"
Placeholder="@(this.IsDisabled ? string.Empty : TA("You can also drag & drop the file here."))"
Adornment="Adornment.Start"
AdornmentIcon="@this.Icon"
AdornmentColor="@(isDropTarget ? Color.Primary : this.IconColor)"
@@ -65,9 +65,9 @@ public partial class ConfigurationInterfaceSize : MSGComponentBase
private string SizeHelp => this.MeasuredScreen switch
{
null => T("AI Studio cannot measure this screen right now, so you cannot set the interface size here."),
{ } screen when this.OffersAppDefault => string.Format(I18N.I.Culture, T("Applies to this screen only ({0}). The app default shows AI Studio in the same size on every laptop. System scaling follows your operating system."), this.DescribeScreen(screen)),
{ } screen => string.Format(I18N.I.Culture, T("Applies to this screen only ({0}). System scaling follows your operating system."), this.DescribeScreen(screen)),
null => TA("AI Studio cannot measure this screen right now, so you cannot set the interface size here."),
{ } screen when this.OffersAppDefault => string.Format(I18N.I.Culture, TA("Applies to this screen only ({0}). The app default shows AI Studio in the same size on every laptop. System scaling follows your operating system."), this.DescribeScreen(screen)),
{ } screen => string.Format(I18N.I.Culture, TA("Applies to this screen only ({0}). System scaling follows your operating system."), this.DescribeScreen(screen)),
};
/// <summary>
@@ -85,8 +85,8 @@ public partial class ConfigurationInterfaceSize : MSGComponentBase
/// a comparison, since the slider starts there when the user comes from the default.
/// </summary>
private string ScaleHelp(InterfaceSizeResponse screen) => this.OffersAppDefault
? string.Format(I18N.I.Culture, T("You can choose from {0} % to {1} %. The app default on this screen is {2} %."), screen.MinPercent, screen.MaxPercent, screen.AppDefaultPercent)
: string.Format(I18N.I.Culture, T("You can choose from {0} % to {1} %. The system scaling on this screen is {2} %."), screen.MinPercent, screen.MaxPercent, screen.SystemPercent);
? string.Format(I18N.I.Culture, TA("You can choose from {0} % to {1} %. The app default on this screen is {2} %."), screen.MinPercent, screen.MaxPercent, screen.AppDefaultPercent)
: string.Format(I18N.I.Culture, TA("You can choose from {0} % to {1} %. The system scaling on this screen is {2} %."), screen.MinPercent, screen.MaxPercent, screen.SystemPercent);
private string DescribeScreen(InterfaceSizeResponse screen) => screen.ScreenDiagonalInches is { } diagonal
? string.Format(I18N.I.Culture, T("{0} × {1} pixels, {2:0.#} inches"), screen.ScreenWidth, screen.ScreenHeight, diagonal)
@@ -1,3 +1,3 @@
@using AIStudio.Settings
@inherits MSGComponentBase
<ConfigurationSelect IsLocked="this.IsLocked" Disabled="this.Disabled" OptionDescription="@T("Select a minimum confidence level")" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" SelectionUpdateAsync="@this.SelectionUpdateAsync" OptionHelp="@T("Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level.")"/>
<ConfigurationSelect IsLocked="this.IsLocked" Disabled="this.Disabled" OptionDescription="@TA("Select a minimum confidence level")" SelectedValue="@this.FilteredSelectedValue" Data="@ConfigurationSelectDataFactory.GetConfidenceLevelsData(this.SettingsManager, this.RestrictToGlobalMinimumConfidence)" SelectionUpdate="@this.SelectionUpdate" SelectionUpdateAsync="@this.SelectionUpdateAsync" OptionHelp="@TA("Choose the minimum confidence level that all LLM providers must meet. This way, you can ensure that only trustworthy providers are used. You cannot use any provider that falls below this level.")"/>
@@ -80,9 +80,9 @@ public partial class ConfigurationMultiSelect<TData> : ConfigurationBaseCore
return this.EmptySelectionText ?? T("No items selected.");
if(selectedValues.Count == 1)
return this.SingleSelectionText ?? T("You have selected 1 item.");
return this.SingleSelectionText ?? TA("You have selected 1 item.");
return string.Format(this.MultipleSelectionText ?? T("You have selected {0} items."), selectedValues.Count);
return string.Format(this.MultipleSelectionText ?? TA("You have selected {0} items."), selectedValues.Count);
}
private bool IsLockedValue(TData value) => this.IsItemLocked(value);
@@ -7,7 +7,7 @@ namespace AIStudio.Components;
public partial class ConfigurationProviderSelection : MSGComponentBase
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationProviderSelection).Namespace, nameof(ConfigurationProviderSelection));
private new static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(ConfigurationProviderSelection).Namespace, nameof(ConfigurationProviderSelection));
[Parameter]
public Func<string> SelectedValue { get; set; } = () => string.Empty;
@@ -19,7 +19,7 @@ public partial class ConfigurationProviderSelection : MSGComponentBase
public IEnumerable<ConfigurationSelectData<string>> Data { get; set; } = new List<ConfigurationSelectData<string>>();
[Parameter]
public Func<string> HelpText { get; set; } = () => TB("Select a provider that is preselected.");
public Func<string> HelpText { get; set; } = () => TA("Select a provider that is preselected.");
[Parameter]
public Tools.Components Component { get; set; } = Tools.Components.NONE;
@@ -5,7 +5,7 @@
<MudTabPanel Text="@T("Back up")" Icon="@AppIcons.BACKUP" Disabled="@(this.isBusy || this.DataRestoreService.IsPreparing)">
<MudText Typo="Typo.body1" Class="mb-3">
@T("Back up all your data into one file: your settings, chats, plugins, and the indexes of your data sources. Your API keys and passwords stay out of it. AI Studio keeps them in the secure storage of your OS.")
@TA("Back up all your data into one file: your settings, chats, plugins, and the indexes of your data sources. Your API keys and passwords stay out of it. AI Studio keeps them in the secure storage of your OS.")
</MudText>
@if (this.DataBackupService.IsRunning)
@@ -20,7 +20,7 @@
</MudButton>
</MudStack>
<MudText Typo="Typo.caption">
@T("The backup goes on when you leave this page.")
@TA("The backup goes on when you leave this page.")
</MudText>
}
else
@@ -42,7 +42,7 @@
@if (!backupResult.IncludesDataSourceIndexes)
{
<MudText Typo="Typo.body2">
@T("The indexes of your data sources are not part of this backup, because they were not available. After a restore, AI Studio indexes your data sources anew.")
@TA("The indexes of your data sources are not part of this backup, because they were not available. After a restore, AI Studio indexes your data sources anew.")
</MudText>
}
</MudAlert>
@@ -66,13 +66,13 @@
<MudTabPanel Text="@T("Restore")" Icon="@AppIcons.RESTORE_BACKUP" Disabled="@(this.isBusy || this.DataBackupService.IsRunning)">
<MudText Typo="Typo.body1" Class="mb-3">
@T("Restore a backup: replace all your data with those of the backup, e.g., on a new computer, or add only its chats to the chats you have.")
@TA("Restore a backup: replace all your data with those of the backup, e.g., on a new computer, or add only its chats to the chats you have.")
</MudText>
@if (this.DataRestoreService.IsRestoreUnfinished)
{
<MudAlert Severity="Severity.Error" Dense="@true" Class="mb-3">
@T("AI Studio has not finished restoring an earlier backup yet. Please restart AI Studio first.")
@TA("AI Studio has not finished restoring an earlier backup yet. Please restart AI Studio first.")
</MudAlert>
}
@@ -120,7 +120,7 @@
@T("Replace all data")
</MudText>
<MudText Typo="Typo.body2" Class="mb-2">
@T("Replaces your settings, chats, plugins, and the indexes of your data sources with those of the backup. AI Studio restarts to do so. The plugins and settings of your organization stay as they are.")
@TA("Replaces your settings, chats, plugins, and the indexes of your data sources with those of the backup. AI Studio restarts to do so. The plugins and settings of your organization stay as they are.")
</MudText>
@if (this.DataRestoreService.IsPreparing)
@@ -145,7 +145,7 @@
@if (this.DataRestoreService.LastResult is { Success: false } restoreResult)
{
<MudAlert Severity="@(restoreResult.Cancelled ? Severity.Info : Severity.Error)" Dense="@true" Class="mt-3">
@(restoreResult.Cancelled ? T("The restore was stopped. All your data stay as they are.") : restoreResult.Issue)
@(restoreResult.Cancelled ? TA("The restore was stopped. All your data stay as they are.") : restoreResult.Issue)
</MudAlert>
}
@@ -153,7 +153,7 @@
@T("Add chats only")
</MudText>
<MudText Typo="Typo.body2" Class="mb-2">
@T("Adds the chats of the backup to the chats you have. Nothing else changes.")
@TA("Adds the chats of the backup to the chats you have. Nothing else changes.")
</MudText>
<MudSelect T="ChatArchiveCollisionBehavior" @bind-Value="@this.collisionBehavior" Label="@T("When a chat already exists")" Variant="Variant.Outlined" Margin="Margin.Dense" Disabled="@this.isBusy" Class="mb-3">
@@ -177,7 +177,7 @@
</MudButton>
</MudStack>
<MudText Typo="Typo.caption">
@T("Please stay on this page until this is done. Leaving the page stops adding the chats.")
@TA("Please stay on this page until this is done. Leaving the page stops adding the chats.")
</MudText>
}
else
@@ -98,7 +98,7 @@ public partial class DataBackupPanel : MSGComponentBase
private string BackupProgressText => this.DataBackupService.Progress switch
{
{ Phase: DataBackupPhase.COPYING_INDEXES } current => string.Format(T("Copying the indexes of your data sources: {0} of {1}"), current.Processed + 1, current.Total),
{ Phase: DataBackupPhase.COPYING_INDEXES } current => string.Format(TA("Copying the indexes of your data sources: {0} of {1}"), current.Processed + 1, current.Total),
{ Phase: DataBackupPhase.WRITING, Total: > 0 } current => string.Format(T("Writing file {0} of {1}"), current.Processed, current.Total),
_ => T("Preparing the backup"),
};
@@ -225,7 +225,7 @@ public partial class DataBackupPanel : MSGComponentBase
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, T("Restoring this backup replaces your settings, your chats, your plugins, and the indexes of your data sources with those of the backup. Whatever you changed since the backup was made is lost. Afterward, AI Studio restarts. Do you want to restore this backup?") },
{ x => x.Message, TA("Restoring this backup replaces your settings, your chats, your plugins, and the indexes of your data sources with those of the backup. Whatever you changed since the backup was made is lost. Afterward, AI Studio restarts. Do you want to restore this backup?") },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Replace All Data"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -12,7 +12,7 @@
<MudText Typo="Typo.h4">
@T("Explanation")
</MudText>
<MudJustifiedMarkdown Value="@T("AI Studio keeps your data in two folders on this computer. When you back up your computer with a tool of your own, such as [Restic](https://restic.net), [Borg](https://www.borgbackup.org), or [Time Machine](https://support.apple.com/en-us/104984), this shows which of their contents belong into the backup and which it can leave out. Click a path to copy it to the clipboard, or use the folder button to open it in your file manager.")"/>
<MudJustifiedMarkdown Value="@TA("AI Studio keeps your data in two folders on this computer. When you back up your computer with a tool of your own, such as [Restic](https://restic.net), [Borg](https://www.borgbackup.org), or [Time Machine](https://support.apple.com/en-us/104984), this shows which of their contents belong in the backup and which it can leave out. Click a path to copy it to the clipboard, or use the folder button to open it in your file manager.")"/>
@foreach (var location in LOCATIONS)
{
@@ -26,7 +26,7 @@
<MudLink OnClick="@(() => this.CopyPath(folder))" Style="min-width: 0; overflow-wrap: anywhere;">
@folder
</MudLink>
<MudTooltip Text="@T("Open in your file manager")">
<MudTooltip Text="@TA("Open in your file manager")">
<MudIconButton Icon="@AppIcons.FOLDER_OPEN"
Size="Size.Medium"
Disabled="@string.IsNullOrWhiteSpace(folder)"
@@ -102,14 +102,14 @@
@T("Good to know")
</MudText>
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
@T("Back up the indexes of your data sources only while AI Studio is closed. AI Studio writes to them while it runs, so a copy made meanwhile may not fit together, and AI Studio would have to embed your data sources anew.")
@TA("Back up the indexes of your data sources only while AI Studio is closed. AI Studio writes to them while it runs, so a copy made meanwhile may not fit together, and AI Studio would have to embed your data sources anew.")
</MudJustifiedText>
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
@T("Your API keys, tokens, and passwords are not in these folders. AI Studio keeps them in the secure storage of your OS. After you restored your data on another computer, enter them there once more.")
@TA("Your API keys, tokens, and passwords are not in these folders. AI Studio keeps them in the secure storage of your OS. After you restored your data on another computer, enter them there once more.")
</MudJustifiedText>
@if (organizationPluginFolders.Count > 0)
{
<MudJustifiedText Class="mb-3" Typo="Typo.body1">
@T("Your organization deployed some plugins among your own ones, for example, with a device management. Their folders do not show this, so the list above names them one by one.")
@TA("Your organization deployed some plugins among your own ones, for example, with a device management. Their folders do not show this, so the list above names them one by one.")
</MudJustifiedText>
}
@@ -43,7 +43,7 @@ public partial class DataFolders : MSGComponentBase
{
var notice = T("The following is meant for experts who back up their computer with a tool of their own.");
return this.SettingsManager.ConfigurationData.App.AllowUserToBackUpData
? $"{notice} {T("To back up your data with AI Studio itself, or to move it to another computer, use Data Backup & Restore on this page or in the settings instead.")}"
? $"{notice} {TA("To back up your data with AI Studio itself, or to move it to another computer, use Data Backup & Restore on this page or in the settings instead.")}"
: notice;
}
}
@@ -75,25 +75,25 @@ public partial class DataFolders : MSGComponentBase
private string Describe(DataBackupEntryKind kind) => kind switch
{
DataBackupEntryKind.SETTINGS => T("Your settings."),
DataBackupEntryKind.WORKSPACES => T("Your chats in workspaces, along with their transcripts."),
DataBackupEntryKind.TEMPORARY_CHATS => T("Your temporary chats, along with their transcripts."),
DataBackupEntryKind.CHAT_PROVENANCE => T("What the AI received in your chats besides your messages, such as the content of attached files, the results of tools, and passages of your data sources. It is part of your chats."),
DataBackupEntryKind.PLUGINS => T("Your plugins, such as the assistants you built, apart from the folders listed below."),
DataBackupEntryKind.TOKENIZERS => T("The tokenizers you chose for your providers."),
DataBackupEntryKind.VISUAL_BRIEFINGS => T("Your projects in the Visual Briefing Assistant."),
DataBackupEntryKind.BEYOND_FACTS_LAB => T("Your preparations in the BeyondFacts Lab, with their material and conversations."),
DataBackupEntryKind.DATA_SOURCE_INDEX => T("The index of your local data sources: which files AI Studio embedded, and their text."),
DataBackupEntryKind.VECTOR_STORES => T("The embeddings of your local data sources."),
DataBackupEntryKind.MEDIA_TRANSCRIPTS => T("The transcripts of the audio and video files in your local data sources."),
DataBackupEntryKind.SETTINGS => TA("Your settings."),
DataBackupEntryKind.WORKSPACES => TA("Your chats in workspaces, along with their transcripts."),
DataBackupEntryKind.TEMPORARY_CHATS => TA("Your temporary chats, along with their transcripts."),
DataBackupEntryKind.CHAT_PROVENANCE => TA("What the AI received in your chats besides your messages, such as the content of attached files, the results of tools, and passages of your data sources. It is part of your chats."),
DataBackupEntryKind.PLUGINS => TA("Your plugins, such as the assistants you built, apart from the folders listed below."),
DataBackupEntryKind.TOKENIZERS => TA("The tokenizers you chose for your providers."),
DataBackupEntryKind.VISUAL_BRIEFINGS => TA("Your projects in the Visual Briefing Assistant."),
DataBackupEntryKind.BEYOND_FACTS_LAB => TA("Your preparations in the BeyondFacts Lab, with their material and conversations."),
DataBackupEntryKind.DATA_SOURCE_INDEX => TA("The index of your local data sources: which files AI Studio embedded, and their text."),
DataBackupEntryKind.VECTOR_STORES => TA("The embeddings of your local data sources."),
DataBackupEntryKind.MEDIA_TRANSCRIPTS => TA("The transcripts of the audio and video files in your local data sources."),
DataBackupEntryKind.SETTINGS_OF_OTHER_VERSIONS => T("Copies of your settings, which every version of AI Studio keeps for itself. Your current settings are all a backup needs."),
DataBackupEntryKind.SETTINGS_OF_OTHER_VERSIONS => TA("Copies of your settings, which every version of AI Studio keeps for itself. Your current settings are all a backup needs."),
DataBackupEntryKind.BUILT_IN_PLUGINS => T("Plugins which come with AI Studio, such as the language packs. AI Studio brings them back by itself."),
DataBackupEntryKind.ORGANIZATION_PLUGINS => T("Plugins of your organization. Your organization brings them back."),
DataBackupEntryKind.ORGANIZATION_PLUGINS => TA("Plugins of your organization. Your organization brings them back."),
DataBackupEntryKind.TEMPORARY_FILES => T("Files which AI Studio needs only for a moment and deletes again."),
DataBackupEntryKind.DOWNLOADED_TOOLS => T("Tools which AI Studio downloaded for this OS, such as Pandoc. On another computer, AI Studio downloads them again."),
DataBackupEntryKind.INTERFACE_KEYS => T("Keys with which AI Studio protects data in its user interface. Only the Flatpak keeps them here, and on another computer, AI Studio creates new ones."),
DataBackupEntryKind.LOGS => T("Log files. They help when something goes wrong, but they hold none of your data."),
DataBackupEntryKind.LOGS => TA("Log files. They help when something goes wrong, but they hold none of your data."),
_ => string.Empty,
};
@@ -37,7 +37,7 @@ public partial class DataSourceAddButtons : MSGComponentBase
/// The rule of the organization comes first: an embedding provider would not make the mailbox possible.
/// </remarks>
private string MailboxMenuText => !this.MayAddMailbox
? T("Mailbox (IMAP), not allowed by your organization")
? TA("Mailbox (IMAP), not allowed by your organization")
: this.HasEmbeddingProvider
? T("Mailbox (IMAP)")
: T("Mailbox (IMAP), requires an embedding provider");
@@ -41,15 +41,15 @@ public partial class DataSourceCloudEmbeddingWarning : MSGComponentBase
private bool SendsMediaToCloudTranscription => !string.IsNullOrWhiteSpace(this.CloudTranscriptionProvider);
private string LabelOn => this.SendsMediaToCloudTranscription
? T("Yes, please send my data to the external providers")
: T("Yes, please send my data to the external embedding provider");
? TA("Yes, please send my data to the external providers")
: TA("Yes, please send my data to the external embedding provider");
private string LabelOff => this.SendsMediaToCloudTranscription
? T("No, I will choose other providers or files")
: T("No, I will choose another embedding");
private string TranscriptionWarningText => string.Format(
T("Warning: Your transcription provider '{0}' is not self-hosted. Audio and video files this data source selects are sent to it to be transcribed, which can cost money. AI Studio keeps every transcript, so an unchanged recording is sent only once. MindWork AI Studio has no control over what that third party does with the recordings after they are sent."),
TA("Warning: Your transcription provider '{0}' is not self-hosted. Audio and video files this data source selects are sent to it to be transcribed, which can cost money. AI Studio keeps every transcript, so an unchanged recording is sent only once. MindWork AI Studio has no control over what that third party does with the recordings after they are sent."),
this.CloudTranscriptionProvider);
private string WarningText
@@ -5,11 +5,11 @@
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@if (this.AreMailboxesEnabled)
{
@T("You might configure different data sources. A data source can include one file, all files in a directory, a mailbox, or data from your company. Later, you can incorporate these data sources as needed when the AI requires this data to complete a certain task.")
@TA("You might configure different data sources. A data source can include one file, all files in a directory, a mailbox, or data from your company. Later, you can incorporate these data sources as needed when the AI requires this data to complete a certain task.")
}
else
{
@T("You might configure different data sources. A data source can include one file, all files in a directory, or data from your company. Later, you can incorporate these data sources as needed when the AI requires this data to complete a certain task.")
@TA("You might configure different data sources. A data source can include one file, all files in a directory, or data from your company. Later, you can incorporate these data sources as needed when the AI requires this data to complete a certain task.")
}
</MudJustifiedText>
@@ -23,7 +23,7 @@
</MudStack>
<MudPaper Class="pa-3 mb-3 border-dashed border rounded-lg">
<ConfigurationSlider T="int" OptionDescription="@T("Shortest pause between indexing steps")" Min="0" Max="@this.MinimumPauseSliderMax" Step="1" Unit="@T("seconds")" Value="@(() => this.SettingsManager.ConfigurationData.App.DataSourceIndexing.MinimumPauseBetweenStepsSeconds)" ValueUpdate="@(value => this.SettingsManager.ConfigurationData.App.DataSourceIndexing.MinimumPauseBetweenStepsSeconds = value)" OptionHelp="@T("AI Studio indexes large data sources in steps of a few minutes. Between two steps, it waits for a random time between the shortest and the longest pause. This spares an embedding server which many people share, for example in your organization. Leave both at 0 seconds for no pause.")" IsLocked="@(() => IsMinimumPauseLocked)"/>
<ConfigurationSlider T="int" OptionDescription="@T("Shortest pause between indexing steps")" Min="0" Max="@this.MinimumPauseSliderMax" Step="1" Unit="@T("seconds")" Value="@(() => this.SettingsManager.ConfigurationData.App.DataSourceIndexing.MinimumPauseBetweenStepsSeconds)" ValueUpdate="@(value => this.SettingsManager.ConfigurationData.App.DataSourceIndexing.MinimumPauseBetweenStepsSeconds = value)" OptionHelp="@TA("AI Studio indexes large data sources in steps of a few minutes. Between two steps, it waits for a random time between the shortest and the longest pause. This spares an embedding server which many people share, for example in your organization. Leave both at 0 seconds for no pause.")" IsLocked="@(() => IsMinimumPauseLocked)"/>
<ConfigurationSlider T="int" OptionDescription="@T("Longest pause between indexing steps")" Min="@this.MaximumPauseSliderMin" Max="@DataDataSourceIndexing.MAX_PAUSE_BETWEEN_STEPS_SECONDS" Step="1" Unit="@T("seconds")" Value="@(() => this.SettingsManager.ConfigurationData.App.DataSourceIndexing.MaximumPauseBetweenStepsSeconds)" ValueUpdate="@(value => this.SettingsManager.ConfigurationData.App.DataSourceIndexing.MaximumPauseBetweenStepsSeconds = value)" OptionHelp="@T("The upper end of the random pause between two indexing steps. AI Studio allows up to 300 seconds (5 minutes).")" IsLocked="@(() => IsMaximumPauseLocked)"/>
</MudPaper>
@@ -99,7 +99,7 @@
}
@if (context.IsEnterpriseConfiguration)
{
<MudTooltip Text="@T("This data source is managed by your organization.")">
<MudTooltip Text="@TA("This data source is managed by your organization.")">
<MudIconButton Color="Color.Info" Icon="@AppIcons.ORGANIZATION" Disabled="true"/>
</MudTooltip>
}
@@ -124,7 +124,7 @@ public partial class DataSourceManagement : MSGComponentBase
if (status.SkippedDocuments == 0 || status.DataSourceType is DataSourceType.MAILBOX)
return status.StateLabel;
return $"{status.StateLabel} — {string.Format(T("Skipped files: {0}. Open Background embeddings to see why and what you can do."), status.SkippedDocuments)}";
return $"{status.StateLabel} — {string.Format(TA("Skipped files: {0}. Open Background embeddings to see why and what you can do."), status.SkippedDocuments)}";
}
private string GetEmbeddingName(IDataSourceBase dataSource)
@@ -208,7 +208,7 @@ public partial class DataSourceManagement : MSGComponentBase
{
await this.DialogService.ShowMessageBoxAsync(
T("Export ERI Data Source"),
T("Kerberos/SSO ERI data sources cannot be exported yet. Please configure them manually in the configuration plugin."),
TA("Kerberos/SSO ERI data sources cannot be exported yet. Please configure them manually in the configuration plugin."),
T("Close"));
return;
}
@@ -248,7 +248,7 @@ public partial class DataSourceManagement : MSGComponentBase
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, T("This ERI data source has an access token configured. Do you want to include the encrypted access token in the export? Note: The recipient will need the same encryption secret to use the access token.") },
{ x => x.Message, TA("This ERI data source has an access token configured. Do you want to include the encrypted access token in the export? Note: The recipient will need the same encryption secret to use the access token.") },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Export Access Token?"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -306,7 +306,7 @@ public partial class DataSourceManagement : MSGComponentBase
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Are you sure you want to delete the data source '{0}' of type '{1}'?"), dataSource.Name, dataSource.Type.DisplayName) },
{ x => x.Message, string.Format(TA("Are you sure you want to delete the data source '{0}' of type '{1}'?"), dataSource.Name, dataSource.Type.DisplayName) },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Data Source"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -353,7 +353,7 @@ public partial class DataSourceManagement : MSGComponentBase
if (!this.DataSourceEmbeddingService.IsIndexingPaused)
return false;
await this.DialogService.ShowMessageBoxAsync(title, T("AI Studio is backing up or restoring the indexes of your data sources right now. Please try again once that is done."), T("Close"));
await this.DialogService.ShowMessageBoxAsync(title, TA("AI Studio is backing up or restoring the indexes of your data sources right now. Please try again once that is done."), T("Close"));
return true;
}
@@ -402,7 +402,7 @@ public partial class DataSourceManagement : MSGComponentBase
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Are you sure you want to delete the mailbox '{0}'? Your mails stay on the server as they are. AI Studio only deletes its index of them and the stored password."), mailbox.Name) },
{ x => x.Message, string.Format(TA("Are you sure you want to delete the mailbox '{0}'? Your mails stay on the server as they are. AI Studio only deletes its index of them and the stored password."), mailbox.Name) },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Mailbox"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -51,14 +51,14 @@ public partial class DataSourceMissingEmbeddingWarning : MSGComponentBase
{
var problem = this.IsInDataSourceDialog
? T("No embedding provider is configured yet, but this data source needs one.")
: T("No embedding provider is configured yet. Local data sources, such as your files and folders, need one: it prepares your data so that the AI can search it.");
: TA("No embedding provider is configured yet. Local data sources, such as your files and folders, need one: it prepares your data so that the AI can search it.");
if (!this.MayAdd)
return $"{problem} {T("Your organization manages the embedding providers and has not provided one yet. Please ask your IT department.")}";
return $"{problem} {TA("Your organization manages the embedding providers and has not provided one yet. Please ask your IT department.")}";
// Above the table, the buttons speak for themselves. In a dialog, the worry is about what was entered so far:
return this.IsInDataSourceDialog
? $"{problem} {T("You can add one right here; everything you entered in this dialog stays as it is.")}"
? $"{problem} {TA("You can add one right here; everything you entered in this dialog stays as it is.")}"
: problem;
}
}
@@ -4,7 +4,7 @@
@if (this.SelectionMode is DataSourceSelectionMode.SELECTION_MODE)
{
<div class="d-flex">
<MudTooltip Text="@T("Select the data you want to use here.")" Placement="Placement.Top">
<MudTooltip Text="@TA("Select the data you want to use here.")" Placement="Placement.Top">
<MudIconButton Icon="@AppIcons.DATA_SOURCE" Class="@this.PopoverButtonClasses" OnClick="@this.ToggleDataSourceSelection"/>
</MudTooltip>
@@ -18,7 +18,7 @@
</MudText>
<PreviewBeta ChipClass=""/>
<MudSpacer/>
<MudTooltip Text="@T("Manage your data sources")" Placement="Placement.Top">
<MudTooltip Text="@TA("Manage your data sources")" Placement="Placement.Top">
<MudIconButton Icon="@AppIcons.SETTINGS" OnClick="@this.OpenSettingsDialog"/>
</MudTooltip>
</MudStack>
@@ -34,7 +34,7 @@
else if (this.SettingsManager.ConfigurationData.DataSources.Count == 0)
{
<MudJustifiedText Typo="Typo.body2" Class="mb-2">
@T("You haven't configured any data sources. To grant the AI access to your data, you need to add such a source. However, if you wish to use data from your device, you first have to set up a so-called embedding. This embedding is necessary so the AI can effectively search your data, find and retrieve the correct information required for each task. In addition to local data, you can also incorporate your company's data. To do so, your company must provide the data through an ERI (External Retrieval Interface).")
@TA("You haven't configured any data sources. To grant the AI access to your data, you need to add such a source. However, if you wish to use data from your device, you first have to set up a so-called embedding. This embedding is necessary so the AI can effectively search your data, find and retrieve the correct information required for each task. In addition to local data, you can also incorporate your company's data. To do so, your company must provide the data through an ERI (External Retrieval Interface).")
</MudJustifiedText>
<MudStack StretchItems="StretchItems.None" AlignItems="AlignItems.Start">
@@ -67,7 +67,7 @@
}
else
{
<MudTextSwitch Label="@T("Semantic Search")" Value="@this.IsSemanticSearchPreferred" LabelOn="@T("Yes, the AI searches your data sources itself, whenever a question calls for it (Semantic Search).")" LabelOff="@T("No, AI Studio searches your data sources with every message, before the AI answers (classic RAG).")" ValueChanged="@this.SemanticSearchChanged" Dense="@true" Disabled="@this.ReadOnly"/>
<MudTextSwitch Label="@T("Semantic Search")" Value="@this.IsSemanticSearchPreferred" LabelOn="@TA("Yes, the AI searches your data sources itself, whenever a question calls for it (Semantic Search).")" LabelOff="@TA("No, AI Studio searches your data sources with every message, before the AI answers (classic RAG).")" ValueChanged="@this.SemanticSearchChanged" Dense="@true" Disabled="@this.ReadOnly"/>
}
<MudTextSwitch Label="@T("AI-based data source selection")" Value="@this.aiBasedSourceSelection" LabelOn="@T("Yes, let the AI decide which data sources are needed.")" LabelOff="@T("No, I manually decide which data source to use.")" ValueChanged="@this.AutoModeChanged" Dense="@true" Disabled="@this.ReadOnly"/>
@@ -82,13 +82,13 @@
{
case true when this.GetListedDataSources().Count == 0:
<MudText Typo="Typo.body2" Class="mb-2">
@T("Your data sources cannot be used with the selected providers due to data privacy or confidence-level requirements, or they are currently unavailable.")
@TA("Your data sources cannot be used with the selected providers due to data privacy or confidence-level requirements, or they are currently unavailable.")
</MudText>
break;
case false when this.GetListedDataSources().Count == 0:
<MudText Typo="Typo.body2" Class="mb-2">
@T("Your data sources cannot be used with the selected providers due to data privacy or confidence-level requirements, or they are currently unavailable.")
@TA("Your data sources cannot be used with the selected providers due to data privacy or confidence-level requirements, or they are currently unavailable.")
</MudText>
break;
@@ -113,7 +113,7 @@
else if (this.DataSourcesAISelected.Count == 0)
{
<MudText Typo="Typo.body2" Class="mb-2">
@T("The AI evaluates each of your inputs to determine whether and which data sources are necessary. Currently, the AI has not selected any source.")
@TA("The AI evaluates each of your inputs to determine whether and which data sources are necessary. Currently, the AI has not selected any source.")
</MudText>
}
@@ -208,12 +208,12 @@ else if (this.SelectionMode is DataSourceSelectionMode.CONFIGURATION_MODE)
<MudTextSwitch Label="@T("Are data sources enabled?")" Value="@this.areDataSourcesEnabled" LabelOn="@T("Yes, I want to use data sources.")" LabelOff="@T("No, I don't want to use data sources.")" ValueChanged="@this.EnabledChanged" Disabled="@(this.ReadOnly || this.IsPreselectedDataSourcesDisabledLocked())"/>
@if (this.areDataSourcesEnabled)
{
<MudTextSwitch Label="@T("Semantic Search")" Value="@this.IsSemanticSearchPreferred" LabelOn="@T("Yes, the AI searches your data sources itself, whenever a question calls for it (Semantic Search).")" LabelOff="@T("No, AI Studio searches your data sources with every message, before the AI answers (classic RAG).")" ValueChanged="@this.SemanticSearchChanged" Disabled="@(this.ReadOnly || this.IsPreselectedDataSourcesRetrievalModeLocked())"/>
<MudTextSwitch Label="@T("Semantic Search")" Value="@this.IsSemanticSearchPreferred" LabelOn="@TA("Yes, the AI searches your data sources itself, whenever a question calls for it (Semantic Search).")" LabelOff="@TA("No, AI Studio searches your data sources with every message, before the AI answers (classic RAG).")" ValueChanged="@this.SemanticSearchChanged" Disabled="@(this.ReadOnly || this.IsPreselectedDataSourcesRetrievalModeLocked())"/>
@* No provider is known here, so this says in general what the chat itself tells about its model: *@
@if (this.IsSemanticSearchPreferred)
{
<MudJustifiedText Typo="Typo.body2" Class="mb-3">
@T("When the model of a chat cannot use tools, AI Studio uses classic RAG instead and searches your data sources with every message.")
@TA("When the model of a chat cannot use tools, AI Studio uses classic RAG instead and searches your data sources with every message.")
</MudJustifiedText>
}
@@ -222,7 +222,7 @@ else if (this.SelectionMode is DataSourceSelectionMode.CONFIGURATION_MODE)
@* Semantic Search may fall back to searching with every message, so the validation is kept, marked as belonging to that way only: *@
<MudPaper Class="pa-3 mb-3 border-dashed border rounded-lg">
<MudJustifiedText Typo="Typo.body2" Class="mb-2">
@T("Only used with classic RAG, which searches your data sources with every message:")
@TA("Only used with classic RAG, which searches your data sources with every message:")
</MudJustifiedText>
<MudTextSwitch Label="@T("AI-based data validation")" Value="@this.aiBasedValidation" LabelOn="@T("Yes, let the AI validate & filter the retrieved data.")" LabelOff="@T("No, use all data retrieved from the data sources.")" ValueChanged="@this.ValidationModeChanged" Disabled="@(this.ReadOnly || this.IsPreselectedDataSourcesAutomaticValidationLocked())"/>
</MudPaper>
@@ -391,12 +391,12 @@ public partial class DataSourceSelection : MSGComponentBase
private string GetSemanticSearchUnavailableMessage() => this.effectiveRetrievalMode.SemanticSearchBlockReason switch
{
ToolOfferBlockReason.TOOLS_SWITCHED_OFF => T("Your organization has switched tools off, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.MODEL_CANNOT_USE_TOOLS => T("The selected model cannot use tools, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.TOOL_SWITCHED_OFF => T("Your organization has switched Semantic Search off, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.TOOL_SWITCHED_OFF_BY_USER => T("You switched Semantic Search off in the tool settings, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.PROVIDER_CONFIDENCE_TOO_LOW => T("The selected provider is not trusted enough for Semantic Search, so AI Studio uses classic RAG instead and searches your data sources with every message."),
_ => T("Semantic Search cannot be used here, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.TOOLS_SWITCHED_OFF => TA("Your organization has switched tools off, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.MODEL_CANNOT_USE_TOOLS => TA("The selected model cannot use tools, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.TOOL_SWITCHED_OFF => TA("Your organization has switched Semantic Search off, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.TOOL_SWITCHED_OFF_BY_USER => TA("You switched Semantic Search off in the tool settings, so AI Studio uses classic RAG instead and searches your data sources with every message."),
ToolOfferBlockReason.PROVIDER_CONFIDENCE_TOO_LOW => TA("The selected provider is not trusted enough for Semantic Search, so AI Studio uses classic RAG instead and searches your data sources with every message."),
_ => TA("Semantic Search cannot be used here, so AI Studio uses classic RAG instead and searches your data sources with every message."),
};
private async Task ValidationModeChanged(bool state)
@@ -40,7 +40,7 @@ public partial class DataSourceSelectionRow : MSGComponentBase
private string GetBlockedTooltip() => this.BlockReason switch
{
DataSourceBlockReason.AWAITING_REINDEX => T("This data source is waiting to be indexed again. Until that is finished, it cannot be searched."),
DataSourceBlockReason.NEEDS_REPAIR => T("The index of this data source cannot be read anymore. Open your data source settings with the gear icon above, then use the repair action there."),
DataSourceBlockReason.NEEDS_REPAIR => TA("The index of this data source cannot be read anymore. Open your data source settings with the gear icon above, then use the repair action there."),
_ => string.Empty,
};
@@ -1,13 +1,13 @@
@using AIStudio.Settings.DataModel
@inherits MSGComponentBase
<WorkspaceNameField WorkspaceName="@this.WorkspaceName" WorkspaceNameChanged="@this.SetWorkspaceName" SelectLabel="@T("Existing workspace (Optional)")" Label="@T("Workspace name (Optional)")" HelperText="@T("Choose an existing workspace or enter a name that should be created when the launcher is opened.")" AdornmentIcon="@(this.OpensTemporaryChat ? AppIcons.TEMPORARY_CHAT : AppIcons.NEW_FOLDER)" AdornmentIconSize="Size.Small" TextFieldClass="mb-1"/>
<WorkspaceNameField WorkspaceName="@this.WorkspaceName" WorkspaceNameChanged="@this.SetWorkspaceName" SelectLabel="@T("Existing workspace (Optional)")" Label="@T("Workspace name (Optional)")" HelperText="@TA("Choose an existing workspace or enter a name that should be created when the launcher is opened.")" AdornmentIcon="@(this.OpensTemporaryChat ? AppIcons.TEMPORARY_CHAT : AppIcons.NEW_FOLDER)" AdornmentIconSize="Size.Small" TextFieldClass="mb-1"/>
@* The tile behaves differently with and without a workspace, so the form says which of the two is
chosen right now instead of only explaining that both are possible. *@
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-3">
@(this.OpensTemporaryChat
? T("Without a workspace, the tile opens a disappearing chat: it belongs to no workspace and is deleted according to your workspace maintenance settings.")
? TA("Without a workspace, the tile opens a disappearing chat: it belongs to no workspace and is deleted according to your workspace maintenance settings.")
: T("The tile opens its chat in this workspace and creates the workspace when it does not exist yet."))
</MudText>
@if (this.OpensTemporaryChat && this.SettingsManager.GetChatTemplateWorkspaceName(this.SelectedChatTemplate) is { } templateWorkspaceName)
@@ -17,7 +17,7 @@
@if (this.ShowOpen)
{
<MudTooltip Text="@T("Open this web page in your browser")" Placement="@this.TooltipPlacement" ShowOnFocus="@false">
<MudTooltip Text="@TA("Open this web page in your browser")" Placement="@this.TooltipPlacement" ShowOnFocus="@false">
<MudIconButton Icon="@AppIcons.OPEN_EXTERNAL" Size="Size.Small" Class="icon-button-compact" Href="@this.Path" Target="_blank"/>
</MudTooltip>
}
@@ -46,7 +46,7 @@
{
@if (this.ShowFileManager)
{
<MudTooltip Text="@T("Show this file in the file manager of your system")" Placement="@this.TooltipPlacement" ShowOnFocus="@false">
<MudTooltip Text="@TA("Show this file in the file manager of your system")" Placement="@this.TooltipPlacement" ShowOnFocus="@false">
<MudIconButton Icon="@AppIcons.FOLDER_OPEN" Size="Size.Small" Class="icon-button-compact" OnClick="@this.ShowInFileManager"/>
</MudTooltip>
}
@@ -29,6 +29,8 @@ public partial class FileActions : MSGComponentBase
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileActions).Namespace, nameof(FileActions));
private new static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(FileActions).Namespace, nameof(FileActions));
/// <summary>
/// What a path names.
/// </summary>
@@ -99,7 +101,7 @@ public partial class FileActions : MSGComponentBase
/// <returns>The tooltip.</returns>
internal static string GetOpenTooltip(int? pageNumber) => pageNumber is { } page
? string.Format(TB("Open the file at page {0}"), page)
: TB("Open the file in the program your system uses for it");
: TA("Open the file in the program your system uses for it");
#region Overrides of ComponentBase
@@ -16,7 +16,7 @@
<MudTextField T="string" InputId="@this.fileInputId" Text="@this.state.FileText" TextChanged="@this.ChangeFilesAsync" Immediate="@true" Disabled="@this.Disabled" Label="@T("Files")" Lines="1" Sizing="InputSizing.Auto" MaxLines="@FilePatternSelection.MAX_FIELD_LINES" Class="pattern-text-field" Variant="Variant.Outlined" Margin="Margin.Dense" Validation="@FileExclusions.ValidateFiles" UserAttributes="@INPUT_ATTRIBUTES" Placeholder="*.min.js;*.webm;draft-*" Adornment="@(this.ShowReset ? Adornment.End : Adornment.None)" AdornmentIcon="@AppIcons.RESTORE_DEFAULT" OnAdornmentClick="@(() => this.RestoreDefaultsAsync(false))" AdornmentAriaLabel="@this.FileRestoreLabel"/>
</MudStack>
<MudText Typo="Typo.caption" Class="mud-text-secondary mt-1">@T("These files and folders are left out, even when a file pattern selects them. Separate entries with a semicolon or a line break. End an entry with a slash to exclude every folder of that name, e.g., node_modules/.")</MudText>
<MudText Typo="Typo.caption" Class="mud-text-secondary mt-1">@TA("These files and folders are left out, even when a file pattern selects them. Separate entries with a semicolon or a line break. End an entry with a slash to exclude every folder of that name, e.g., node_modules/.")</MudText>
<MudCollapse Expanded="@(this.ShowHelp && this.showHelp)">
<div class="mt-2">
<FilePatternHelp ShowInclusionExamples="@false" ShowExclusionExamples="@true"/>
@@ -2,7 +2,7 @@
<MudPaper Outlined="@true" Class="pa-3" Elevation="0">
<MudText Typo="Typo.subtitle2">@T("Pattern examples")</MudText>
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-2">@T("Separate patterns with semicolons. Exclusions take priority over included files.")</MudText>
<MudText Typo="Typo.body2" Class="mud-text-secondary mb-2">@TA("Separate patterns with semicolons. Exclusions take priority over included files.")</MudText>
<MudSimpleTable Dense="@true" Elevation="0" aria-label="@T("Pattern examples")">
<tbody>
<tr><td><code>*.pdf;*.docx</code></td><td>@T("PDF and Word files")</td></tr>
@@ -5,10 +5,10 @@ namespace AIStudio.Components;
public partial class HalluzinationReminder: ComponentBase
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(HalluzinationReminder).Namespace, nameof(HalluzinationReminder));
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(HalluzinationReminder).Namespace, nameof(HalluzinationReminder));
[Parameter]
public string Text { get; set; } = TB("LLMs can make mistakes. Check important information.");
public string Text { get; set; } = TA("LLMs can make mistakes. Check important information.");
[Parameter]
public string ContainerClass { get; set; } = "mt-2 mb-1";
@@ -52,6 +52,20 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IAsyncDispo
/// <returns>The text from the language plugin or the English text.</returns>
public string TA(string fallbackEN) => this.GetText(this.Lang, fallbackEN, addressForm: I18N.I.AddressForm);
/// <summary>
/// Gets a text which addresses the reader, in the form of address the person chose, under the key of the given
/// type; see I18N.AddressForm.
/// </summary>
/// <remarks>
/// A base class, such as AssistantBase, needs this for its own texts: the collector files them under the base
/// class, while the component at runtime is a derived one.
/// </remarks>
/// <param name="fallbackEN">The text in English (US).</param>
/// <param name="typeNamespace">The namespace of the type requesting the text, used as part of the key.</param>
/// <param name="typeName">The name of the type requesting the text, used as part of the key.</param>
/// <returns>The text from the language plugin or the English text.</returns>
public string TA(string fallbackEN, string? typeNamespace, string? typeName) => this.GetText(this.Lang, fallbackEN, typeNamespace, typeName, I18N.I.AddressForm);
#region Implementation of IMessageBusReceiver
public async Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
@@ -61,9 +75,10 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IAsyncDispo
switch (triggeredEvent)
{
case Event.COLOR_THEME_CHANGED:
case Event.ADDRESS_FORM_CHANGED:
this.StateHasChanged();
break;
case Event.PLUGINS_RELOADED:
this.Lang = await this.SettingsManager.GetActiveLanguagePlugin();
await this.InvokeAsync(this.StateHasChanged);
@@ -112,10 +127,11 @@ public abstract class MSGComponentBase : ComponentBase, IDisposable, IAsyncDispo
/// <param name="events">A list of events for which you want to receive messages.</param>
protected void ApplyFilters(ComponentBase[] filterComponents, Event[] events)
{
// Append the color theme changed event to the list of events:
// Append the events which every component handles to the list of events:
var eventsList = new List<Event>(events)
{
Event.COLOR_THEME_CHANGED,
Event.ADDRESS_FORM_CHANGED,
Event.PLUGINS_RELOADED,
};
@@ -49,8 +49,8 @@ public partial class ManagePandocDependency : MSGComponentBase
private string ButtonText => this.IsOutdated ? T("Update Pandoc") : T("Install Pandoc");
private string PromptText => this.IsOutdated
? $"{this.IntroText} {T("Your Pandoc installation is outdated. Please update it to the latest version to ensure compatibility with all features.")}"
: $"{this.IntroText} {T("Pandoc is not installed or not available. Please install it to use the features that require Pandoc.")}";
? $"{this.IntroText} {TA("Your Pandoc installation is outdated. Please update it to the latest version to ensure compatibility with all features.")}"
: $"{this.IntroText} {TA("Pandoc is not installed or not available. Please install it to use the features that require Pandoc.")}";
private async Task ShowPandocDialogAsync()
{
@@ -4,7 +4,7 @@
{
@* Nothing runs at all, so the other two would only add noise: *@
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="@true" Class="@this.Class">
@T("Tools were selected for this run, but the chosen model cannot use tools. It runs without them. Please choose a model which supports tools.")
@TA("Tools were selected for this run, but the chosen model cannot use tools. It runs without them. Please choose a model which supports tools.")
</MudAlert>
}
else
@@ -13,21 +13,21 @@ else
@if (this.ToolsNeedingConfiguration.Count > 0)
{
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="@true" Class="@this.Class">
@(string.Format(T("Some tools selected for this run are not fully configured and stay unused: {0}. Please complete their settings."), string.Join(", ", this.ToolsNeedingConfiguration)))
@(string.Format(TA("Some tools selected for this run are not fully configured and stay unused: {0}. Please complete their settings."), string.Join(", ", this.ToolsNeedingConfiguration)))
</MudAlert>
}
@if (this.ToolsBeyondProviderConfidence.Count > 0)
{
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="@true" Class="@this.Class">
@(string.Format(T("Not all tools selected for this run can be used with the chosen AI provider: {0}. Please choose a provider with a higher confidence level to use all of them."), string.Join(", ", this.ToolsBeyondProviderConfidence)))
@(string.Format(TA("Not all tools selected for this run can be used with the chosen AI provider: {0}. Please choose a provider with a higher confidence level to use all of them."), string.Join(", ", this.ToolsBeyondProviderConfidence)))
</MudAlert>
}
@if (this.ToolsSwitchedOffByUser.Count > 0)
{
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="@true" Class="@this.Class">
@(string.Format(T("Some tools selected for this run are switched off in your tool settings and stay unused: {0}. Switch them on again in the app settings to use them."), string.Join(", ", this.ToolsSwitchedOffByUser)))
@(string.Format(TA("Some tools selected for this run are switched off in your tool settings and stay unused: {0}. Switch them on again in the app settings to use them."), string.Join(", ", this.ToolsSwitchedOffByUser)))
</MudAlert>
}
}
@@ -16,7 +16,7 @@
else if (this.AcceptanceStatus is MandatoryInfoAcceptanceStatus.VERSION_CHANGED)
{
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="@true">
@T("A new version of the terms is available. Please review it again.")
@TA("A new version of the terms is available. Please review it again.")
<br />
@T("Last accepted version"): @this.Acceptance!.AcceptedVersion
<br />
@@ -26,7 +26,7 @@
else if (this.AcceptanceStatus is MandatoryInfoAcceptanceStatus.CONTENT_CHANGED)
{
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Dense="@true">
@T("Please review this text again. The content was changed.")
@TA("Please review this text again. The content was changed.")
<br />
@T("Last accepted version"): @this.Acceptance!.AcceptedVersion
<br />
@@ -36,5 +36,5 @@
</MudText>
<MudText Typo="Typo.body1" Class="mb-3" Style="text-align: justify; hyphens: auto;">
@T("We also want to contribute to the democratization of AI. MindWork AI Studio runs even on low-cost hardware, including computers around 100 EUR such as Raspberry Pi. This makes the app and its full feature set accessible to people and families with limited budgets. You can start with local LLMs for your first steps or use affordable cloud models. MindWork AI Studio itself is available free of charge.")
@TA("We also want to contribute to the democratization of AI. MindWork AI Studio runs even on low-cost hardware, including computers around 100 EUR such as Raspberry Pi. This makes the app and its full feature set accessible to people and families with limited budgets. You can start with local LLMs for your first steps or use affordable cloud models. MindWork AI Studio itself is available free of charge.")
</MudText>
@@ -64,7 +64,7 @@ public partial class OutboundDataRestrictionSelection : MSGComponentBase
return T("Only self-hosted providers may use this data source. A chat which found something in it may therefore only use the services configured in AI Studio, such as the allowed private hosts of the tool 'Read Web Page'.");
if (this.OrganizationMinimum is not OutboundDataRestriction.UNRESTRICTED)
return T("Your organization does not allow less strict settings than the ones offered here.");
return TA("Your organization does not allow less strict settings than the ones offered here.");
return null;
}
@@ -149,8 +149,8 @@ public partial class PluginDeleteAction : MSGComponentBase
: 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);
? string.Format(this.TA("Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files."), this.Plugin.Name)
: string.Format(this.TA("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<ConfirmDialog>
{
@@ -2,7 +2,7 @@
@inherits MSGComponentBase
<MudStack Row="true" AlignItems="AlignItems.Baseline" StretchItems="StretchItems.Start" Class="mb-3" Wrap="Wrap.NoWrap">
<MudSelect T="Profile" Strict="@true" Disabled="@this.Disabled" Value="@this.Profile" ValueChanged="@this.SelectionChanged" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.PROFILE" Margin="Margin.Dense" Label="@T("Select one of your profiles")" Variant="Variant.Outlined" Class="mb-3" Validation="@this.Validation">
<MudSelect T="Profile" Strict="@true" Disabled="@this.Disabled" Value="@this.Profile" ValueChanged="@this.SelectionChanged" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.PROFILE" Margin="Margin.Dense" Label="@TA("Select one of your profiles")" Variant="Variant.Outlined" Class="mb-3" Validation="@this.Validation">
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
{
<MudSelectItem Value="profile">
@@ -15,7 +15,7 @@
}
</ActivatorContent>
<ChildContent>
<MudMenuItem Icon="@AppIcons.SETTINGS" Label="@T("Manage your profiles")" OnClick="@(async () => await this.OpenSettingsDialog())" />
<MudMenuItem Icon="@AppIcons.SETTINGS" Label="@TA("Manage your profiles")" OnClick="@(async () => await this.OpenSettingsDialog())" />
<MudDivider/>
@foreach (var profile in this.SettingsManager.ConfigurationData.Profiles.GetAllProfiles())
{
@@ -9,7 +9,7 @@ namespace AIStudio.Components;
public partial class ProfileSelection : MSGComponentBase
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ProfileSelection).Namespace, nameof(ProfileSelection));
private new static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(ProfileSelection).Namespace, nameof(ProfileSelection));
[Parameter]
public Profile CurrentProfile { get; set; } = Profile.NO_PROFILE;
@@ -32,7 +32,7 @@ public partial class ProfileSelection : MSGComponentBase
[Inject]
private IDialogService DialogService { get; init; } = null!;
private readonly string defaultToolTipText = TB("You can switch between your profiles here");
private readonly string defaultToolTipText = TA("You can switch between your profiles here");
private string ToolTipText => this.Disabled ? this.DisabledText : this.defaultToolTipText;
@@ -90,12 +90,12 @@ public partial class ProviderSelection : MSGComponentBase
var configuredProviders = this.SettingsManager.GetAllProviders().Where(x => x.UsedLLMProvider is not LLMProviders.NONE).ToList();
if (configuredProviders.Count == 0)
return this.T("No LLM providers are configured yet. Add a provider in the app settings.");
return this.TA("No LLM providers are configured yet. Add a provider in the app settings.");
if (configuredProviders.All(x => this.SettingsManager.IsProviderProhibited(x.UsedLLMProvider)))
return this.T("The use of all your LLM providers has been prohibited by your organization. Add a different provider in the app settings.");
return this.TA("The use of all your LLM providers has been prohibited by your organization. Add a different provider in the app settings.");
return this.T("No LLM providers meet the confidence requirements. Configure an eligible provider in the app settings.");
return this.TA("No LLM providers meet the confidence requirements. Configure an eligible provider in the app settings.");
}
private IReadOnlyList<CapabilityIcon> GetCapabilityIcons(AIStudio.Settings.Provider provider)
@@ -219,7 +219,7 @@ public partial class ReadFileContent : MSGComponentBase
this.Logger.LogWarning("Could not access {Count} dropped file(s): {Paths}", inaccessiblePaths.Count, string.Join(", ", inaccessiblePaths));
await this.MessageBus.SendWarning(new(
AppIcons.WARNING,
this.T("Some dropped files could not be accessed. Please select them with the file chooser instead.")));
this.TA("Some dropped files could not be accessed. Please select them with the file chooser instead.")));
}
foreach (var path in paths)
@@ -240,7 +240,7 @@ public partial class ReadFileContent : MSGComponentBase
if (this.Filter is { Length: > 0 } && !FileTypes.IsAllowedPath(filePath, this.Filter))
{
this.Logger.LogWarning("Selected file does not match the configured file type filter: '{FilePath}'", filePath);
await this.MessageBus.SendWarning(new(AppIcons.WARNING, this.T("Please select a file with a supported file type.")));
await this.MessageBus.SendWarning(new(AppIcons.WARNING, this.TA("Please select a file with a supported file type.")));
return false;
}
@@ -292,7 +292,7 @@ public partial class ReadFileContent : MSGComponentBase
{
await this.MessageBus.SendWarning(new(
AppIcons.VOICE_TRANSCRIPTION,
this.T("Media files require a configured transcription provider. Configure one in the transcription settings.")));
this.TA("Media files require a configured transcription provider. Configure one in the transcription settings.")));
return false;
}
@@ -3,7 +3,7 @@
<MudTextSwitch Label="@T("Read content from web?")" Disabled="@this.AgentIsRunning" Value="@this.Preselect" ValueChanged="@this.ShowWebContentReaderChanged" LabelOn="@T("Show web content options")" LabelOff="@T("Hide web content options")" />
@if (this.Preselect)
{
<MudTextSwitch Label="@T("Cleanup content by using an LLM agent?")" Value="@this.PreselectContentCleanerAgent" ValueChanged="@this.UseContentCleanerAgentChanged" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" />
<MudTextSwitch Label="@T("Clean up content by using an LLM agent?")" Value="@this.PreselectContentCleanerAgent" ValueChanged="@this.UseContentCleanerAgentChanged" Disabled="@this.AgentIsRunning" LabelOn="@T("The content is cleaned using an LLM agent: the main content is extracted, advertisements and other irrelevant things are attempted to be removed; relative links are attempted to be converted into absolute links so that they can be used.")" LabelOff="@T("No content cleaning")" />
@if (this.ContentCleanerHint is { } contentCleanerHint)
{
<MudText Typo="Typo.body2" Color="Color.Error" Class="mb-3">
@@ -11,7 +11,7 @@
</MudText>
}
<MudStack Row="@true" AlignItems="@AlignItems.Baseline" Class="mb-3">
<MudTextField T="string" Label="@T("URL from which to load the content")" Value="@this.URL" ValueChanged="@this.URLValueChanged" Validation="@this.ValidateURL" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.LINK" Placeholder="https://..." HelperText="@T("Loads the content from your URL. Does not work when the content is hidden behind a paywall.")" Variant="Variant.Outlined" Immediate="@true" Disabled="@this.AgentIsRunning"/>
<MudTextField T="string" Label="@T("URL from which to load the content")" Value="@this.URL" ValueChanged="@this.URLValueChanged" Validation="@this.ValidateURL" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.LINK" Placeholder="https://..." HelperText="@TA("Loads the content from your URL. Does not work when the content is hidden behind a paywall.")" Variant="Variant.Outlined" Immediate="@true" Disabled="@this.AgentIsRunning"/>
<MudButton Disabled="@(!this.IsReady || this.AgentIsRunning)" Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" StartIcon="@AppIcons.DOWNLOAD" OnClick="@this.LoadFromWeb">
@T("Fetch")
</MudButton>
@@ -280,23 +280,23 @@ public partial class ReadWebContent : MSGComponentBase
return null;
if(this.ProviderSettings == AIStudio.Settings.Provider.NONE)
return T("The content cleaner uses the model of this assistant. Please select one below.");
return TA("The content cleaner uses the model of this assistant. Please select one below.");
return T("The selected model does not meet the confidence requirements of the content cleaner. Please select another model, or configure an eligible one in the app settings.");
return TA("The selected model does not meet the confidence requirements of the content cleaner. Please select another model, or configure an eligible one in the app settings.");
}
}
private string? ValidateURL(string url)
{
if(string.IsNullOrWhiteSpace(url))
return T("Please provide a URL to load the content from.");
return TA("Please provide a URL to load the content from.");
var urlParsingResult = Uri.TryCreate(url, UriKind.Absolute, out var uriResult);
if(!urlParsingResult)
return T("Please provide a valid URL.");
return TA("Please provide a valid URL.");
if(uriResult is not { Scheme: "http" or "https" })
return T("Please provide a valid HTTP or HTTPS URL.");
return TA("Please provide a valid HTTP or HTTPS URL.");
return null;
}
@@ -5,7 +5,7 @@
<PathDropZone CatchAllDocuments="@this.CatchAllDocuments" Disabled="@(() => this.Disabled)" OnPathsDropped="@this.PathsDropped">
@this.Picker
<MudText Typo="Typo.body2">
@T("You can also drag & drop the folder here.")
@TA("You can also drag & drop the folder here.")
</MudText>
</PathDropZone>
}
@@ -5,7 +5,7 @@
<PathDropZone CatchAllDocuments="@this.CatchAllDocuments" Disabled="@(() => this.Disabled)" OnPathsDropped="@this.PathsDropped">
@this.Picker
<MudText Typo="Typo.body2">
@T("You can also drag & drop the file here.")
@TA("You can also drag & drop the file here.")
</MudText>
</PathDropZone>
}
@@ -11,10 +11,10 @@
@(this.SettingsManager.ConfigurationData.AssistantPluginAudit.RequireAuditBeforeActivation ? T("External Assistants must be audited before activation") : T("External Assistant can be activated without an audit"))
</MudSwitch>
</MudField>
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider = selectedValue)" HelpText="@(() => T("Optionally choose a dedicated provider for assistant plugin audits. When left empty, AI Studio falls back to the app-wide default provider."))" />
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider = selectedValue)" HelpText="@(() => TA("Optionally choose a dedicated provider for assistant plugin audits. When left empty, AI Studio falls back to the app-wide default provider."))" />
<ConfigurationSelect OptionDescription="@T("Minimum required audit level")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel)" Data="@ConfigurationSelectDataFactory.GetAssistantAuditLevelsData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel = selectedValue)" OptionHelp="@T("External Assistants rated below this audit level are treated as insufficiently reviewed.")" />
<ConfigurationOption OptionDescription="@T("Block activation below the minimum Audit-Level?")" LabelOn="@T("Activation is blocked below the minimum Audit-Level")" LabelOff="@T("Users may still activate plugins below the minimum Audit-Level")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum = updatedState)"
OptionHelp="@T("The audit shows you all security risks and information, if you consider this rating false at your own discretion, you can decide to install it anyway (not recommended).")"/>
OptionHelp="@TA("The audit shows you all security risks and information. If you consider this rating false at your own discretion, you can decide to install it anyway (not recommended).")"/>
<ConfigurationOption OptionDescription="@T("Automatically audit new or updated plugins in the background?")" LabelOn="@T("Security audit is automatically done in the background")" LabelOff="@T("Security audit is done manually by the user")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants = updatedState)" />
</MudPaper>
</ExpansionPanel>
@@ -13,7 +13,7 @@ public partial class SettingsPanelAgentAssistantAudit : SettingsPanelBase
{
{
x => x.Message,
this.T("Disabling this setting turns off assistant plugin security audits. External assistants may then be activated and used even without a valid audit or after plugin changes. Do you really want to disable this protection?")
this.TA("Disabling this setting turns off assistant plugin security audits. External assistants may then be activated and used even without a valid audit or after plugin changes. Do you really want to disable this protection?")
},
};
@@ -5,7 +5,7 @@
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("Use Case: this agent is used to clean up text content. It extracts the main content, removes advertisements and other irrelevant things, and attempts to convert relative links into absolute links so that they can be used.")
</MudJustifiedText>
<ConfigurationOption OptionDescription="@T("Preselect text content cleaner options?")" LabelOn="@T("Options are preselected")" LabelOff="@T("No options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions = updatedState)" OptionHelp="@T("When enabled, you can preselect some agent options. This is might be useful when you prefer an LLM.")"/>
<ConfigurationOption OptionDescription="@T("Preselect text content cleaner options?")" LabelOn="@T("Options are preselected")" LabelOff="@T("No options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions = updatedState)" OptionHelp="@TA("When enabled, you can preselect some agent options. This might be useful when you prefer an LLM.")"/>
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.TextContentCleaner.PreselectedAgentProvider = selectedValue)"/>
</MudPaper>
</ExpansionPanel>
@@ -6,7 +6,7 @@
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("Use Case: this agent is used to select the appropriate data sources for the current prompt.")
</MudJustifiedText>
<ConfigurationOption OptionDescription="@T("Preselect data source selection options?")" LabelOn="@T("Options are preselected")" LabelOff="@T("No options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions = updatedState)" OptionHelp="@T("When enabled, you can preselect some agent options. This is might be useful when you prefer an LLM.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentDataSourceSelection, x => x.PreselectAgentOptions, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Preselect data source selection options?")" LabelOn="@T("Options are preselected")" LabelOff="@T("No options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions = updatedState)" OptionHelp="@TA("When enabled, you can preselect some agent options. This might be useful when you prefer an LLM.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentDataSourceSelection, x => x.PreselectAgentOptions, out var meta) && meta.IsLocked"/>
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AgentDataSourceSelection.PreselectedAgentProvider = selectedValue)" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentDataSourceSelection, x => x.PreselectedAgentProvider, out var meta) && meta.IsLocked"/>
</MudPaper>
</ExpansionPanel>
@@ -3,14 +3,14 @@
<ExpansionPanel HeaderIcon="@AppIcons.AGENT_RETRIEVAL_CONTEXT_VALIDATION" HeaderText="@T("Agent: Retrieval Context Validation Options")">
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("Use Case: this agent is used to validate any retrieval context of any retrieval process. Perhaps there are many of these retrieval contexts and you want to validate them all. Therefore, you might want to use a cheap and fast LLM for this job. When using a local or self-hosted LLM, look for a small (e.g. 3B) and fast model.")
@TA("Use Case: this agent is used to validate any retrieval context of any retrieval process. Perhaps there are many of these retrieval contexts and you want to validate them all. Therefore, you might want to use a cheap and fast LLM for this job. When using a local or self-hosted LLM, look for a small (e.g. 3B) and fast model.")
</MudJustifiedText>
<ConfigurationOption OptionDescription="@T("Enable the retrieval context validation agent?")" LabelOn="@T("The validation agent is enabled")" LabelOff="@T("No validation is performed")" State="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation = updatedState)" OptionHelp="@T("When enabled, the retrieval context validation agent will check each retrieval context of any retrieval process, whether a context makes sense for the given prompt.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentRetrievalContextValidation, x => x.EnableRetrievalContextValidation, out var meta) && meta.IsLocked"/>
@if (this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.EnableRetrievalContextValidation)
{
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<ConfigurationOption OptionDescription="@T("Preselect retrieval context validation options?")" LabelOn="@T("Options are preselected")" LabelOff="@T("No options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions = updatedState)" OptionHelp="@T("When enabled, you can preselect some agent options. This is might be useful when you prefer an LLM.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentRetrievalContextValidation, x => x.PreselectAgentOptions, out var meta) && meta.IsLocked"/>
<ConfigurationSlider T="int" OptionDescription="@T("How many validation agents should work simultaneously?")" Min="1" Max="100" Step="1" Unit="@T("agents")" Value="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations = updatedValue)" OptionHelp="@T("More active agents also mean that a corresponding number of requests are made simultaneously. Some providers limit the number of requests per minute. When you are unsure, choose a low setting between 1 to 6 agents.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentRetrievalContextValidation, x => x.NumParallelValidations, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Preselect retrieval context validation options?")" LabelOn="@T("Options are preselected")" LabelOff="@T("No options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions = updatedState)" OptionHelp="@TA("When enabled, you can preselect some agent options. This might be useful when you prefer an LLM.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentRetrievalContextValidation, x => x.PreselectAgentOptions, out var meta) && meta.IsLocked"/>
<ConfigurationSlider T="int" OptionDescription="@T("How many validation agents should work simultaneously?")" Min="1" Max="100" Step="1" Unit="@T("agents")" Value="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.NumParallelValidations = updatedValue)" OptionHelp="@TA("More active agents also mean that a corresponding number of requests are made simultaneously. Some providers limit the number of requests per minute. When you are unsure, choose a low setting between 1 to 6 agents.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentRetrievalContextValidation, x => x.NumParallelValidations, out var meta) && meta.IsLocked"/>
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" Disabled="@(() => !this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectAgentOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AgentRetrievalContextValidation.PreselectedAgentProvider = selectedValue)" IsLocked="() => ManagedConfiguration.TryGet(x => x.AgentRetrievalContextValidation, x => x.PreselectedAgentProvider, out var meta) && meta.IsLocked"/>
</MudPaper>
}
@@ -5,43 +5,48 @@
<ExpansionPanel HeaderIcon="@AppIcons.APP" HeaderText="@T("App Options")">
<ConfigurationSelect OptionDescription="@T("Language behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguageBehavior)" Data="@ConfigurationSelectDataFactory.GetLangBehaviorData()" SelectionUpdate="@(selectedValue => this.UpdateLangBehaviour(selectedValue).Observe(nameof(this.UpdateLangBehaviour)))" OptionHelp="@T("Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?")"/>
<ConfigurationSelect OptionDescription="@T("Language behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguageBehavior)" Data="@ConfigurationSelectDataFactory.GetLangBehaviorData()" SelectionUpdate="@(selectedValue => this.UpdateLangBehaviour(selectedValue).Observe(nameof(this.UpdateLangBehaviour)))" OptionHelp="@TA("Select the language behavior for the app. The default is to use the system language. You might want to choose a language manually?")"/>
@if (this.SettingsManager.ConfigurationData.App.LanguageBehavior is LangBehavior.MANUAL)
{
<ConfigurationSelect OptionDescription="@T("Language")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(selectedValue => this.UpdateManuallySelectedLanguage(selectedValue).Observe(nameof(this.UpdateManuallySelectedLanguage)))" OptionHelp="@T("Select the language for the app.")"/>
<ConfigurationSelect OptionDescription="@T("Language")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.LanguagePluginId)" Data="@ConfigurationSelectDataFactory.GetLanguagesData()" SelectionUpdate="@(selectedValue => this.UpdateManuallySelectedLanguage(selectedValue).Observe(nameof(this.UpdateManuallySelectedLanguage)))" OptionHelp="@TA("Select the language for the app.")"/>
}
<ConfigurationSelect OptionDescription="@T("Color theme")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="@T("Choose the color theme that best suits for you.")"/>
@if (this.offersFormOfAddress)
{
<ConfigurationSelect OptionDescription="@T("Form of address")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.AddressForm)" Data="@ConfigurationSelectDataFactory.GetAddressFormData()" SelectionUpdate="@(selectedValue => this.UpdateAddressForm(selectedValue).Observe(nameof(this.UpdateAddressForm)))" OptionHelp="@TA("Whether AI Studio addresses you formally or informally.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.AddressForm, out var meta) && meta.IsLocked"/>
}
<ConfigurationSelect OptionDescription="@T("Color theme")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreferredTheme)" Data="@ConfigurationSelectDataFactory.GetThemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreferredTheme = selectedValue)" OptionHelp="@TA("Choose the color theme that best suits for you.")"/>
<ConfigurationInterfaceSize/>
<ConfigurationOption OptionDescription="@T("Save energy?")" LabelOn="@T("Energy saving is enabled")" LabelOff="@T("Energy saving is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.IsSavingEnergy)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.IsSavingEnergy = updatedState)" OptionHelp="@T("When enabled, streamed content from the AI is updated once every third second. When disabled, streamed content will be updated as soon as it is available.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.IsSavingEnergy, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Enable spellchecking?")" LabelOn="@T("Spellchecking is enabled")" LabelOff="@T("Spellchecking is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="@T("When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections.")"/>
<ConfigurationOption OptionDescription="@T("Close messages about exported files automatically?")" LabelOn="@T("The message closes after a few seconds")" LabelOff="@T("The message stays until you close it")" State="@(() => this.SettingsManager.ConfigurationData.App.CloseExportMessagesAutomatically)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.CloseExportMessagesAutomatically = updatedState)" OptionHelp="@T("After you export a file, for example, an answer or a presentation, a message offers to show the file in your file manager or to open it. When enabled, this message closes after a few seconds, like other messages. When disabled, it stays until you close it.")"/>
<ConfigurationOption OptionDescription="@T("Enable spellchecking?")" LabelOn="@T("Spellchecking is enabled")" LabelOff="@T("Spellchecking is disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.EnableSpellchecking)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.EnableSpellchecking = updatedState)" OptionHelp="@TA("When enabled, spellchecking will be active in all input fields. Depending on your operating system, errors may not be visually highlighted, but right-clicking may still offer possible corrections.")"/>
<ConfigurationOption OptionDescription="@T("Close messages about exported files automatically?")" LabelOn="@T("The message closes after a few seconds")" LabelOff="@TA("The message stays until you close it")" State="@(() => this.SettingsManager.ConfigurationData.App.CloseExportMessagesAutomatically)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.CloseExportMessagesAutomatically = updatedState)" OptionHelp="@TA("After you export a file, for example, an answer or a presentation, a message offers to show the file in your file manager or to open it. When enabled, this message closes after a few seconds, like other messages. When disabled, it stays until you close it.")"/>
<ConfigurationOption OptionDescription="@T("Show details when suspicious content was removed?")" LabelOn="@T("A dialog lists what was removed and explains the attack pattern")" LabelOff="@T("Only a short notification is shown")" State="@(() => this.SettingsManager.ConfigurationData.App.ShowPromptInjectionAlert)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.ShowPromptInjectionAlert = updatedState)" OptionHelp="@T("Shows a dialog listing the removed passages, together with an explanation and an external reference.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShowPromptInjectionAlert, out var meta) && meta.IsLocked"/>
<ConfigurationSlider T="int" OptionDescription="@T("Request timeout")" Min="@ExternalHttpClientTimeout.MIN_HTTP_CLIENT_TIMEOUT_SECONDS" Max="@ExternalHttpClientTimeout.MAX_HTTP_CLIENT_TIMEOUT_SECONDS" Step="60" Unit="@T("seconds")" Value="@(() => this.SettingsManager.ConfigurationData.App.HttpClientTimeoutSeconds)" ValueUpdate="@(updatedValue => this.SettingsManager.ConfigurationData.App.HttpClientTimeoutSeconds = updatedValue)" OptionHelp="@T("How long AI Studio waits for external HTTP requests, such as AI providers, embeddings, transcription, ERI data sources, and enterprise configuration downloads.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.HttpClientTimeoutSeconds, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Share feature usage with operators of self-hosted servers?")" LabelOn="@T("Operators of self-hosted servers learn which feature sent a request")" LabelOff="@T("No usage details are sent")" State="@(() => this.SettingsManager.ConfigurationData.App.ShareFeatureUsageWithSelfHostedServerOperators)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.ShareFeatureUsageWithSelfHostedServerOperators = updatedState)" OptionHelp="@T("When enabled, requests to self-hosted providers and LiteLLM name the feature that sent them, e.g., the chat, an assistant, or an agent, together with the version of AI Studio and your operating system. This lets the operators of these servers, usually your organization, see which features are used. Cloud providers never receive these details, and AI Studio never sends anything to its developers.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShareFeatureUsageWithSelfHostedServerOperators, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@TA("Share feature usage with operators of self-hosted servers?")" LabelOn="@T("Operators of self-hosted servers learn which feature sent a request")" LabelOff="@T("No usage details are sent")" State="@(() => this.SettingsManager.ConfigurationData.App.ShareFeatureUsageWithSelfHostedServerOperators)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.ShareFeatureUsageWithSelfHostedServerOperators = updatedState)" OptionHelp="@TA("When enabled, requests to self-hosted providers and LiteLLM name the feature that sent them, e.g., the chat, an assistant, or an agent, together with the version of AI Studio and your operating system. This lets the operators of these servers, usually your organization, see which features are used. Cloud providers never receive these details, and AI Studio never sends anything to its developers.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShareFeatureUsageWithSelfHostedServerOperators, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Check for updates")" SelectedValue="@(() => this.DisplayedUpdateInterval)" Data="@ConfigurationSelectDataFactory.GetManagedUpdateIntervalData(this.DisplayedUpdateInterval)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateInterval = selectedValue)" OptionHelp="@this.UpdateIntervalHelp" IsLocked="@this.IsUpdateIntervalLocked"/>
<ConfigurationSelect OptionDescription="@T("Update installation method")" SelectedValue="@(() => this.DisplayedUpdateInstallation)" Data="@ConfigurationSelectDataFactory.GetUpdateBehaviourData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UpdateInstallation = selectedValue)" OptionHelp="@this.UpdateInstallationHelp" IsLocked="@this.IsUpdateInstallationLocked"/>
<ConfigurationSelect OptionDescription="@T("Navigation bar behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="@T("Select the desired behavior for the navigation bar.")"/>
<ConfigurationSelect OptionDescription="@T("Navigation bar behavior")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.NavigationBehavior)" Data="@ConfigurationSelectDataFactory.GetNavBehaviorData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.NavigationBehavior = selectedValue)" OptionHelp="@TA("Select the desired behavior for the navigation bar.")"/>
<ConfigurationSelect OptionDescription="@T("Start page")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.StartPage)" Data="@ConfigurationSelectDataFactory.GetStartPageData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.StartPage = selectedValue)" OptionHelp="@this.GetStartPageHelpText()" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.StartPage, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Show administration settings?")" LabelOn="@T("Administration settings are visible")" LabelOff="@T("Administration settings are not visible")" State="@(() => this.SettingsManager.ConfigurationData.App.ShowAdminSettings)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.ShowAdminSettings = updatedState)" OptionHelp="@T("When enabled, additional administration options become visible. These options are intended for IT staff to manage organization-wide configuration, e.g. configuring and exporting providers for an entire organization.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ShowAdminSettings, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Add a notice to AI-generated content?")" LabelOn="@T("AI-generated content gets a notice")" LabelOff="@T("AI-generated content gets no notice")" State="@(() => this.SettingsManager.ConfigurationData.App.AddAIGeneratedContentDisclosure)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.AddAIGeneratedContentDisclosure = updatedState)" OptionHelp="@T("When enabled, AI answers which you copy or export end with a notice that they were generated using artificial intelligence (AI), in the language of the app. The same holds for the files of the Batch Processing Assistant. Code blocks, single files or tables taken from an answer, visual briefings, and the results of the Prompt Optimizer and the Assistant Builder stay unchanged. Rules on transparency, such as the EU AI Act, may require such a notice, for example, when you publish a text.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.AddAIGeneratedContentDisclosure, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Preview feature visibility")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="@T("Do you want to show preview features in the app?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreviewVisibility, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Add a notice to AI-generated content?")" LabelOn="@T("AI-generated content gets a notice")" LabelOff="@T("AI-generated content gets no notice")" State="@(() => this.SettingsManager.ConfigurationData.App.AddAIGeneratedContentDisclosure)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.AddAIGeneratedContentDisclosure = updatedState)" OptionHelp="@TA("When enabled, AI answers which you copy or export end with a notice that they were generated using artificial intelligence (AI), in the language of the app. The same holds for the files of the Batch Processing Assistant. Code blocks, single files or tables taken from an answer, visual briefings, and the results of the Prompt Optimizer and the Assistant Builder stay unchanged. Rules on transparency, such as the EU AI Act, may require such a notice, for example, when you publish a text.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.AddAIGeneratedContentDisclosure, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Preview feature visibility")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreviewVisibility)" Data="@ConfigurationSelectDataFactory.GetPreviewVisibility()" SelectionUpdate="@this.UpdatePreviewFeatures" OptionHelp="@TA("Do you want to show preview features in the app?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreviewVisibility, out var meta) && meta.IsLocked"/>
@if (this.SettingsManager.ConfigurationData.App.PreviewVisibility > PreviewVisibility.NONE)
{
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
if (availablePreviewFeatures.Count > 0)
{
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@this.GetSelectedPreviewFeatures" Data="@availablePreviewFeatures" SelectionUpdate="@this.UpdateEnabledPreviewFeatures" OptionHelp="@T("Which preview features would you like to enable?")" IsItemLocked="@this.IsPluginContributedPreviewFeature" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.IsLocked" EmptySelectionText="@T("No preview features selected.")" SingleSelectionText="@T("You have selected 1 preview feature.")" MultipleSelectionText="@T("You have selected {0} preview features.")"/>
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@this.GetSelectedPreviewFeatures" Data="@availablePreviewFeatures" SelectionUpdate="@this.UpdateEnabledPreviewFeatures" OptionHelp="@TA("Which preview features would you like to enable?")" IsItemLocked="@this.IsPluginContributedPreviewFeature" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.IsLocked" EmptySelectionText="@T("No preview features selected.")" SingleSelectionText="@TA("You have selected 1 preview feature.")" MultipleSelectionText="@TA("You have selected {0} preview features.")"/>
}
}
<ConfigurationProviderSelection Component="Components.APP_SETTINGS" Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => T("Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence."))" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProvider, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Preselect one of your profiles?")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="@T("Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProfile, out var meta) && meta.IsLocked"/>
<ConfigurationProviderSelection Component="Components.APP_SETTINGS" Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProvider = selectedValue)" HelpText="@(() => TA("Would you like to set one provider as the default for the entire app? When you configure a different provider for an assistant, it will always take precedence."))" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProvider, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@TA("Preselect one of your profiles?")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.PreselectedProfile)" Data="@ConfigurationSelectDataFactory.GetProfilesData(this.SettingsManager.ConfigurationData.Profiles)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.PreselectedProfile = selectedValue)" OptionHelp="@TA("Would you like to set one of your profiles as the default for the entire app? When you configure a different profile for an assistant, it will always take precedence.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.PreselectedProfile, out var meta) && meta.IsLocked"/>
@if (PreviewFeatures.PRE_SPEECH_TO_TEXT_2026.IsEnabled(this.SettingsManager))
{
<ConfigurationSelect OptionDescription="@T("Select a transcription provider")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider)" Data="@this.GetFilteredTranscriptionProviders()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider = selectedValue)" OptionHelp="@T("Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UseTranscriptionProvider, out var meta) && meta.IsLocked">
<ConfigurationSelect OptionDescription="@TA("Select a transcription provider")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider)" Data="@this.GetFilteredTranscriptionProviders()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.UseTranscriptionProvider = selectedValue)" OptionHelp="@TA("Select a transcription provider for transcribing your voice. Without a selected provider, dictation and transcription features will be disabled.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.UseTranscriptionProvider, out var meta) && meta.IsLocked">
<ItemTemplate Context="providerData">
@if (this.GetTranscriptionProvider(providerData.Value) is { } provider)
{
@@ -64,9 +69,9 @@
</MudText>
<MudJustifiedText Typo="Typo.body2" Class="mb-3">
@T("Generate a 256-bit encryption secret for encrypting API keys in configuration plugins. Deploy this secret to client machines via Group Policy (Windows Registry) or environment variables. Providers can then be exported with encrypted API keys using the export buttons in the provider settings.")
@TA("Generate a 256-bit encryption secret for encrypting API keys in configuration plugins. Deploy this secret to client machines via Group Policy (Windows Registry) or environment variables. Providers can then be exported with encrypted API keys using the export buttons in the provider settings.")
<MudLink Href="https://github.com/MindWorkAI/AI-Studio/wiki/Enterprise-IT" Target="_blank">
@T("Read the Enterprise IT documentation for details.")
@TA("Read the Enterprise IT documentation for details.")
</MudLink>
</MudJustifiedText>
@@ -78,14 +83,14 @@
@T("Generate an encryption secret and copy it to the clipboard")
</MudButton>
<ConfigurationSelect OptionDescription="@T("Format for encrypted secrets in exports")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.EnterpriseEncryptionExportFormat)" Data="@ConfigurationSelectDataFactory.GetEnterpriseEncryptionFormatData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnterpriseEncryptionExportFormat = selectedValue)" OptionHelp="@T("Applies when you export providers, data sources, or tool settings with their API keys, tokens, or passwords. The protected format makes AI Studio refuse every value which was encrypted with another secret, but versions before v26.10.1 cannot read it. Keep the compatible format until every computer in your organization runs v26.10.1 or newer. Both formats use the same encryption secret.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnterpriseEncryptionExportFormat, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@T("Format for encrypted secrets in exports")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.App.EnterpriseEncryptionExportFormat)" Data="@ConfigurationSelectDataFactory.GetEnterpriseEncryptionFormatData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnterpriseEncryptionExportFormat = selectedValue)" OptionHelp="@TA("Applies when you export providers, data sources, or tool settings with their API keys, tokens, or passwords. The protected format makes AI Studio refuse every value which was encrypted with another secret, but versions before v26.10.1 cannot read it. Keep the compatible format until every computer in your organization runs v26.10.1 or newer. Both formats use the same encryption secret.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnterpriseEncryptionExportFormat, out var meta) && meta.IsLocked"/>
<MudText Typo="Typo.h6" Class="mt-6 mb-3">
@T("External HTTPS certificates")
</MudText>
<ConfigurationOption OptionDescription="@T("Use additional root certificates for external HTTPS requests?")" LabelOn="@T("Additional root certificates are enabled")" LabelOff="@T("Additional root certificates are disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificatesEnabled)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificatesEnabled = updatedState)" OptionHelp="@T("When enabled, AI Studio can trust root certificates from a configured PEM bundle for external HTTPS requests, such as self-hosted AI providers, embeddings, transcription, ERI data sources, and enterprise configuration downloads. Normal hostname and certificate validity checks still apply. Integrated cloud providers, such as OpenAI, Google, and others, will never use these additional certificates. Please note that you usually do not need this setting on macOS or Windows. If you use Linux with the AppImage version of MindWork AI Studio, you also do not need this option. A valid use case is a Linux environment where AI Studio runs from a Flatpak.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificatesEnabled, out var meta) && meta.IsLocked"/>
<ConfigurationFile OptionDescription="@T("Root certificate bundle path")" Icon="@AppIcons.CERTIFICATE" Text="@(() => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificateBundlePath)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificateBundlePath = updatedText)" FileDialogTitle="@T("Select a root certificate bundle")" Filter="@([FileTypes.CERTIFICATE_BUNDLE])" Disabled="@this.AreExternalHttpCustomRootCertificateDetailsDisabled" OptionHelp="@T("Path to a PEM file containing one or more root CA certificates. For Flatpak deployments, this file must be placed in a location that is readable inside the sandbox.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificateBundlePath, out var meta) && meta.IsLocked"/>
<ConfigurationText OptionDescription="@T("Allowed hosts for additional root certificates")" Icon="@AppIcons.SERVER" NumLines="3" Text="@this.GetExternalHttpCustomRootCertificateAllowedHostsText" TextUpdate="@this.UpdateExternalHttpCustomRootCertificateAllowedHosts" Disabled="@this.AreExternalHttpCustomRootCertificateDetailsDisabled" OptionHelp="@T("Enter one host pattern per line. Exact hosts such as data.intra.example.org and one-label wildcards such as *.intra.example.org are supported. Cloud provider endpoints built into AI Studio, such as OpenAI, Google, etc., never use these additional root certificates.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificateAllowedHosts, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Use additional root certificates for external HTTPS requests?")" LabelOn="@T("Additional root certificates are enabled")" LabelOff="@T("Additional root certificates are disabled")" State="@(() => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificatesEnabled)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificatesEnabled = updatedState)" OptionHelp="@TA("When enabled, AI Studio can trust root certificates from a configured PEM bundle for external HTTPS requests, such as self-hosted AI providers, embeddings, transcription, ERI data sources, and enterprise configuration downloads. Normal hostname and certificate validity checks still apply. Integrated cloud providers, such as OpenAI, Google, and others, will never use these additional certificates. Please note that you usually do not need this setting on macOS or Windows. If you use Linux with the AppImage version of MindWork AI Studio, you also do not need this option. A valid use case is a Linux environment where AI Studio runs from a Flatpak.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificatesEnabled, out var meta) && meta.IsLocked"/>
<ConfigurationFile OptionDescription="@T("Root certificate bundle path")" Icon="@AppIcons.CERTIFICATE" Text="@(() => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificateBundlePath)" TextUpdate="@(updatedText => this.SettingsManager.ConfigurationData.App.ExternalHttpCustomRootCertificateBundlePath = updatedText)" FileDialogTitle="@TA("Select a root certificate bundle")" Filter="@([FileTypes.CERTIFICATE_BUNDLE])" Disabled="@this.AreExternalHttpCustomRootCertificateDetailsDisabled" OptionHelp="@T("Path to a PEM file containing one or more root CA certificates. For Flatpak deployments, this file must be placed in a location that is readable inside the sandbox.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificateBundlePath, out var meta) && meta.IsLocked"/>
<ConfigurationText OptionDescription="@T("Allowed hosts for additional root certificates")" Icon="@AppIcons.SERVER" NumLines="3" Text="@this.GetExternalHttpCustomRootCertificateAllowedHostsText" TextUpdate="@this.UpdateExternalHttpCustomRootCertificateAllowedHosts" Disabled="@this.AreExternalHttpCustomRootCertificateDetailsDisabled" OptionHelp="@TA("Enter one host pattern per line. Exact hosts such as data.intra.example.org and one-label wildcards such as *.intra.example.org are supported. Cloud provider endpoints built into AI Studio, such as OpenAI, Google, etc., never use these additional root certificates.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.ExternalHttpCustomRootCertificateAllowedHosts, out var meta) && meta.IsLocked"/>
}
</ExpansionPanel>
@@ -1,5 +1,6 @@
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
@@ -14,6 +15,12 @@ public partial class SettingsPanelApp : SettingsPanelBase
private UpdatePolicyMode updatePolicyMode;
/// <summary>
/// Whether the language of the app tells a formal address from an informal one, so that the form of address
/// is a choice at all.
/// </summary>
private bool offersFormOfAddress;
private bool CannotUpdateItself => this.updatePolicyMode is UpdatePolicyMode.FLATPAK or UpdatePolicyMode.MANAGED_INSTALLATION or UpdatePolicyMode.UNSUPPORTED_INSTALLATION_LOCATION or UpdatePolicyMode.DEVELOPMENT;
private UpdateInterval DisplayedUpdateInterval => this.CannotUpdateItself
@@ -26,9 +33,9 @@ public partial class SettingsPanelApp : SettingsPanelBase
private string UpdateIntervalHelp => this.updatePolicyMode switch
{
UpdatePolicyMode.ENTERPRISE_DISABLED => T("Your organization has disabled update checks and installations."),
UpdatePolicyMode.ENTERPRISE_DISABLED => TA("Your organization has disabled update checks and installations."),
UpdatePolicyMode.FLATPAK => T("AI Studio cannot check for updates when running as a Flatpak. Updates are managed outside the app."),
UpdatePolicyMode.MANAGED_INSTALLATION => T("This installation does not check for updates itself. Contact the person or organization that installed AI Studio for update information."),
UpdatePolicyMode.MANAGED_INSTALLATION => TA("This installation does not check for updates itself. Contact the person or organization that installed AI Studio for update information."),
UpdatePolicyMode.UNSUPPORTED_INSTALLATION_LOCATION => T("AI Studio cannot update itself from its current location, so it does not check for updates."),
UpdatePolicyMode.DEVELOPMENT => T("Development builds do not check for updates."),
_ => T("How often should we check for app updates?")
@@ -36,10 +43,10 @@ public partial class SettingsPanelApp : SettingsPanelBase
private string UpdateInstallationHelp => this.updatePolicyMode switch
{
UpdatePolicyMode.ENTERPRISE_DISABLED => T("This setting has no effect while updates are disabled by your organization."),
UpdatePolicyMode.FLATPAK => T("AI Studio cannot install updates when running as a Flatpak. Update it using the Flatpak source or bundle from which you installed it."),
UpdatePolicyMode.MANAGED_INSTALLATION => T("AI Studio cannot install updates into this installation. Contact the person or organization that installed it for new versions."),
UpdatePolicyMode.UNSUPPORTED_INSTALLATION_LOCATION => T("AI Studio cannot install updates into its current installation location. Install new versions yourself."),
UpdatePolicyMode.ENTERPRISE_DISABLED => TA("This setting has no effect while updates are disabled by your organization."),
UpdatePolicyMode.FLATPAK => TA("AI Studio cannot install updates when running as a Flatpak. Update it using the Flatpak source or bundle from which you installed it."),
UpdatePolicyMode.MANAGED_INSTALLATION => TA("AI Studio cannot install updates into this installation. Contact the person or organization that installed it for new versions."),
UpdatePolicyMode.UNSUPPORTED_INSTALLATION_LOCATION => TA("AI Studio cannot install updates into its current installation location. Install new versions yourself."),
UpdatePolicyMode.DEVELOPMENT => T("Development builds do not install updates."),
_ => T("Should updates be installed automatically or manually?")
};
@@ -55,6 +62,7 @@ public partial class SettingsPanelApp : SettingsPanelBase
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.GLOBAL_SHORTCUT_CHANGED ]);
await base.OnInitializedAsync();
this.updatePolicyMode = this.UpdatePolicy.CurrentMode;
this.offersFormOfAddress = (await this.SettingsManager.GetActiveLanguagePlugin()).HasInformalAddress();
}
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
@@ -65,6 +73,13 @@ public partial class SettingsPanelApp : SettingsPanelBase
if (triggeredEvent is Event.GLOBAL_SHORTCUT_CHANGED)
this.StateHasChanged();
// Another language might offer the choice, or no longer:
if (triggeredEvent is Event.PLUGINS_RELOADED)
{
this.offersFormOfAddress = (await this.SettingsManager.GetActiveLanguagePlugin()).HasInformalAddress();
this.StateHasChanged();
}
await base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data);
}
@@ -90,11 +105,11 @@ public partial class SettingsPanelApp : SettingsPanelBase
private string GetStartPageHelpText()
{
var helpText = T("Choose which page AI Studio should open first when you start the app. Changes take effect the next time you launch AI Studio.");
var helpText = TA("Choose which page AI Studio should open first when you start the app. Changes take effect the next time you launch AI Studio.");
if (!ManagedConfiguration.TryGet(x => x.App, x => x.StartPage, out var meta) || meta.ManagedMode is not ManagedConfigurationMode.EDITABLE_DEFAULT)
return helpText;
return $"{helpText} {T("Your organization provided a default start page, but you can still change it.")}";
return $"{helpText} {TA("Your organization provided a default start page, but you can still change it.")}";
}
private IEnumerable<ConfigurationSelectData<string>> GetFilteredTranscriptionProviders()
@@ -194,4 +209,10 @@ public partial class SettingsPanelApp : SettingsPanelBase
this.SettingsManager.ConfigurationData.App.LanguagePluginId = pluginId;
await this.MessageBus.SendMessage<bool>(this, Event.PLUGINS_RELOADED);
}
private async Task UpdateAddressForm(AddressForm addressForm)
{
this.SettingsManager.ConfigurationData.App.AddressForm = addressForm;
await this.MessageBus.SendMessage<bool>(this, Event.ADDRESS_FORM_CHANGED);
}
}
@@ -7,19 +7,19 @@
@T("Provider Confidence")
</MudText>
<MudJustifiedText Class="mb-3">
@T("Do you want to always see how trustworthy your providers are? This way, you stay in control of which provider you send your data to. You can choose a common schema or configure the trust levels for each provider yourself.")
@TA("Do you want to always see how trustworthy your providers are? This way, you stay in control of which provider you send your data to. You can choose a common schema or configure the trust levels for each provider yourself.")
</MudJustifiedText>
<ConfigurationOption OptionDescription="@T("Do you want to enforce an global minimum confidence level?")" LabelOn="@T("Yes, enforce a minimum confidence level")" LabelOff="@T("No, do not enforce a minimum confidence level")" State="@(() => this.SettingsManager.ConfigurationData.Confidence.EnforceGlobalMinimumConfidence)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Confidence.EnforceGlobalMinimumConfidence = updatedState)" OptionHelp="@T("When enabled, you can enforce a minimum confidence level for all features in AI Studio. This way, you can make sure only trustworthy providers are used.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.Confidence, x => x.EnforceGlobalMinimumConfidence, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@TA("Do you want to enforce an global minimum confidence level?")" LabelOn="@T("Yes, enforce a minimum confidence level")" LabelOff="@T("No, do not enforce a minimum confidence level")" State="@(() => this.SettingsManager.ConfigurationData.Confidence.EnforceGlobalMinimumConfidence)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Confidence.EnforceGlobalMinimumConfidence = updatedState)" OptionHelp="@TA("When enabled, you can enforce a minimum confidence level for all features in AI Studio. This way, you can make sure only trustworthy providers are used.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.Confidence, x => x.EnforceGlobalMinimumConfidence, out var meta) && meta.IsLocked"/>
@if(this.SettingsManager.ConfigurationData.Confidence.EnforceGlobalMinimumConfidence)
{
<ConfigurationMinConfidenceSelection RestrictToGlobalMinimumConfidence="@false" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Confidence.GlobalMinimumConfidence)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Confidence.GlobalMinimumConfidence = selectedValue)" IsLocked="() => ManagedConfiguration.TryGet(x => x.Confidence, x => x.GlobalMinimumConfidence, out var meta) && meta.IsLocked"/>
}
<ConfigurationOption OptionDescription="@T("Show provider's confidence level?")" LabelOn="@T("Yes, show me the confidence level")" LabelOff="@T("No, please hide the confidence level")" State="@(() => this.SettingsManager.ConfigurationData.Confidence.ShowProviderConfidence)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Confidence.ShowProviderConfidence = updatedState)" OptionHelp="@T("When enabled, we show you the confidence level for the selected provider in the app. This helps you assess where you are sending your data at any time. Example: are you currently working with sensitive data? Then choose a particularly trustworthy provider, etc.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.Confidence, x => x.ShowProviderConfidence, out var meta) && meta.IsLocked"/>
<ConfigurationOption OptionDescription="@T("Show provider's confidence level?")" LabelOn="@T("Yes, show me the confidence level")" LabelOff="@T("No, please hide the confidence level")" State="@(() => this.SettingsManager.ConfigurationData.Confidence.ShowProviderConfidence)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Confidence.ShowProviderConfidence = updatedState)" OptionHelp="@TA("When enabled, we show you the confidence level for the selected provider in the app. This helps you assess where you are sending your data at any time. Example: are you currently working with sensitive data? Then choose a particularly trustworthy provider, etc.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.Confidence, x => x.ShowProviderConfidence, out var meta) && meta.IsLocked"/>
@if (this.SettingsManager.ConfigurationData.Confidence.ShowProviderConfidence)
{
<ConfigurationSelect OptionDescription="@T("Select a confidence scheme")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Confidence.ConfidenceScheme)" Data="@ConfigurationSelectDataFactory.GetConfidenceSchemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Confidence.ConfidenceScheme = selectedValue)" OptionHelp="@T("Choose the scheme that best suits you and your organization. Do you trust any western provider? Or only providers from the USA or exclusively European providers? Then choose the appropriate scheme. Alternatively, you can assign the confidence levels to each provider yourself.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.Confidence, x => x.ConfidenceScheme, out var meta) && meta.IsLocked"/>
<ConfigurationSelect OptionDescription="@TA("Select a confidence scheme")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Confidence.ConfidenceScheme)" Data="@ConfigurationSelectDataFactory.GetConfidenceSchemesData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Confidence.ConfidenceScheme = selectedValue)" OptionHelp="@TA("Choose the scheme that best suits you and your organization. Do you trust any western provider? Or only providers from the USA or exclusively European providers? Then choose the appropriate scheme. Alternatively, you can assign the confidence levels to each provider yourself.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.Confidence, x => x.ConfidenceScheme, out var meta) && meta.IsLocked"/>
@if (this.SettingsManager.ConfigurationData.Confidence.ConfidenceScheme is ConfidenceSchemes.CUSTOM)
{
<MudTable Items="@(Enum.GetValues<LLMProviders>().Where(x => x is not LLMProviders.NONE))" Hover="@true" Class="border-dashed border rounded-lg">
@@ -42,7 +42,7 @@
@if (this.SettingsManager.IsProviderProhibited(context))
{
<MudAlert Severity="Severity.Warning" Dense="@true" Class="mt-2">
@T("The use of this provider has been prohibited by your organization. Therefore, it is untrusted, and you cannot change its confidence level.")
@TA("The use of this provider has been prohibited by your organization. Therefore, it is untrusted, and you cannot change its confidence level.")
</MudAlert>
}
</MudTd>
@@ -15,7 +15,7 @@
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints. For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that discuss similar topics or experiences, even if they use different words.")
@TA("This helps AI Studio understand and compare things in a way that's similar to how humans do. When you're working on something, AI Studio can automatically identify related documents and data by comparing their digital fingerprints. For instance, if you're writing about customer service, AI Studio can instantly find other documents in your data that discuss similar topics or experiences, even if they use different words.")
</MudJustifiedText>
<MudTable Items="@this.SettingsManager.GetAllEmbeddingProviders()" Hover="@true" GroupBy="@GROUP_CONFIG" Class="border-dashed border rounded-lg">
<ColGroup>
@@ -46,25 +46,25 @@
<MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap">
@if (this.SettingsManager.IsProviderProhibited(context.UsedLLMProvider))
{
<MudTooltip Text="@T("The use of this embedding provider has been prohibited by your organization.")">
<MudTooltip Text="@TA("The use of this embedding provider has been prohibited by your organization.")">
<MudIconButton Color="Color.Error" Icon="@AppIcons.BLOCKED" Disabled="true"/>
</MudTooltip>
}
@if (context.IsTrustedForDataSourceSecurityChecks(this.SettingsManager))
{
<MudTooltip Text="@(context.IsSelfHosted ? T("This self-hosted embedding provider is trusted for data source security checks. Local data can be sent to it without security warnings.") : T("This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings."))">
<MudTooltip Text="@(context.IsSelfHosted ? T("This self-hosted embedding provider is trusted for data source security checks. Local data can be sent to it without security warnings.") : TA("This embedding provider is trusted by your organization for data source security checks. Local data can be sent to it without security warnings."))">
<MudIconButton Color="Color.Success" Icon="@AppIcons.TRUSTED" Disabled="true"/>
</MudTooltip>
}
@if (context.IsEnterpriseConfiguration && !context.AllowUserProvidedAPIKey)
{
<MudTooltip Text="@T("This embedding provider is managed by your organization.")">
<MudTooltip Text="@TA("This embedding provider is managed by your organization.")">
<MudIconButton Color="Color.Info" Icon="@AppIcons.ORGANIZATION" Disabled="true"/>
</MudTooltip>
}
else if (context.IsEnterpriseConfiguration && context.AllowUserProvidedAPIKey)
{
<MudTooltip Text="@T("This embedding provider is managed by your organization. You can set your own API key.")">
<MudTooltip Text="@TA("This embedding provider is managed by your organization. You can set your own API key.")">
<MudIconButton Color="Color.Info" Icon="@AppIcons.KEY" OnClick="@(() => this.EditEmbeddingProvider(context))"/>
</MudTooltip>
<MudTooltip Text="@T("Test")">
@@ -108,7 +108,7 @@ public partial class SettingsPanelEmbeddings : SettingsPanelProviderBase
private async Task DeleteEmbeddingProvider(EmbeddingProvider provider)
{
var question = string.Format(T("Are you sure you want to delete the embedding provider '{0}'?"), provider.Name);
var question = string.Format(TA("Are you sure you want to delete the embedding provider '{0}'?"), provider.Name);
var affectedDataSources = DataSourceReindexWarning.DescribeDataSourcesLosingTheirProvider(this.SettingsManager, provider);
//
@@ -142,7 +142,7 @@ public partial class SettingsPanelEmbeddings : SettingsPanelProviderBase
{
var issueDialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Couldn't delete the embedding provider '{0}'. The issue: {1}. We can ignore this issue and delete the embedding provider anyway. Do you want to ignore it and delete this embedding provider?"), provider.Name, deleteSecretResponse.Issue) },
{ x => x.Message, string.Format(TA("Couldn't delete the embedding provider '{0}'. The issue: {1}. We can ignore this issue and delete the embedding provider anyway. Do you want to ignore it and delete this embedding provider?"), provider.Name, deleteSecretResponse.Issue) },
};
var issueDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Embedding Provider"), issueDialogParameters, DialogOptions.FULLSCREEN);
@@ -9,6 +9,8 @@ public abstract class SettingsPanelProviderBase : SettingsPanelBase
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(SettingsPanelProviderBase).Namespace, nameof(SettingsPanelProviderBase));
private new static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(SettingsPanelProviderBase).Namespace, nameof(SettingsPanelProviderBase));
/// <summary>
/// Exports the provider configuration as Lua code, optionally including the encrypted API key if the provider has one
/// configured and the user agrees to include it. The exportFunc should generate the Lua code based on the provided
@@ -29,7 +31,7 @@ public abstract class SettingsPanelProviderBase : SettingsPanelBase
// Ask the user if they want to export the API key:
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, TB("This provider has an API key configured. Do you want to include the encrypted API key in the export? Note: The recipient will need the same encryption secret to use the API key.") },
{ x => x.Message, TA("This provider has an API key configured. Do you want to include the encrypted API key in the export? Note: The recipient will need the same encryption secret to use the API key.") },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(TB("Export API Key?"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -8,7 +8,7 @@
@T("Configured LLM Providers")
</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("What we call a provider is the combination of an LLM provider such as OpenAI and a model like GPT-6 Luna. You can configure as many providers as you want. This way, you can use the appropriate model for each task. As an LLM provider, you can also choose local providers. However, to use this app, you must configure at least one provider.")
@TA("What we call a provider is the combination of an LLM provider such as OpenAI and a model like GPT-6 Luna. You can configure as many providers as you want. This way, you can use the appropriate model for each task. As an LLM provider, you can also choose local providers. However, to use this app, you must configure at least one provider.")
</MudJustifiedText>
<MudTable Items="@this.SettingsManager.GetAllProviders()" Hover="@true" GroupBy="@GROUP_CONFIG" Class="border-dashed border rounded-lg">
<ColGroup>
@@ -38,25 +38,25 @@
<MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap">
@if (this.SettingsManager.IsProviderProhibited(context.UsedLLMProvider))
{
<MudTooltip Text="@T("The use of this provider has been prohibited by your organization.")">
<MudTooltip Text="@TA("The use of this provider has been prohibited by your organization.")">
<MudIconButton Color="Color.Error" Icon="@AppIcons.BLOCKED" Disabled="true"/>
</MudTooltip>
}
@if (context.IsTrustedForDataSourceSecurityChecks(this.SettingsManager))
{
<MudTooltip Text="@(context.IsSelfHosted ? T("This self-hosted provider is trusted for data source security checks.") : T("This provider is trusted by your organization for data source security checks."))">
<MudTooltip Text="@(context.IsSelfHosted ? T("This self-hosted provider is trusted for data source security checks.") : TA("This provider is trusted by your organization for data source security checks."))">
<MudIconButton Color="Color.Success" Icon="@AppIcons.TRUSTED" Disabled="true"/>
</MudTooltip>
}
@if (context.IsEnterpriseConfiguration && !context.AllowUserProvidedAPIKey)
{
<MudTooltip Text="@T("This provider is managed by your organization.")">
<MudTooltip Text="@TA("This provider is managed by your organization.")">
<MudIconButton Color="Color.Info" Icon="@AppIcons.ORGANIZATION" Disabled="true"/>
</MudTooltip>
}
else if (context.IsEnterpriseConfiguration && context.AllowUserProvidedAPIKey)
{
<MudTooltip Text="@T("This provider is managed by your organization. You can set your own API key.")">
<MudTooltip Text="@TA("This provider is managed by your organization. You can set your own API key.")">
<MudIconButton Color="Color.Info" Icon="@AppIcons.KEY" OnClick="@(() => this.EditLLMProvider(context))"/>
</MudTooltip>
}
@@ -149,7 +149,7 @@ public partial class SettingsPanelProviders : SettingsPanelProviderBase
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Are you sure you want to delete the provider '{0}'?"), provider.InstanceName) },
{ x => x.Message, string.Format(TA("Are you sure you want to delete the provider '{0}'?"), provider.InstanceName) },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete LLM Provider"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -174,7 +174,7 @@ public partial class SettingsPanelProviders : SettingsPanelProviderBase
{
var issueDialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Couldn't delete the provider '{0}'. The issue: {1}. We can ignore this issue and delete the provider anyway. Do you want to ignore it and delete this provider?"), provider.InstanceName, deleteSecretResponse.Issue) },
{ x => x.Message, string.Format(TA("Couldn't delete the provider '{0}'. The issue: {1}. We can ignore this issue and delete the provider anyway. Do you want to ignore it and delete this provider?"), provider.InstanceName, deleteSecretResponse.Issue) },
};
var issueDialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete LLM Provider"), issueDialogParameters, DialogOptions.FULLSCREEN);
@@ -2,7 +2,7 @@
<ExpansionPanel HeaderIcon="@AppIcons.TOOL" HeaderText="@T("Tool Settings")">
<MudText Typo="Typo.body1" Class="mb-4">
@T("Configure global settings for each tool.") @T("Switch off the tools you never need: they then appear in no tool selection, and the AI cannot use them.")
@TA("Configure global settings for each tool.") @TA("Switch off the tools you never need: they then appear in no tool selection, and the AI cannot use them.")
</MudText>
<MudTable Items="@this.items" Hover="@true" Dense="@true">
@@ -44,13 +44,13 @@
@if (IsSwitchedOffByUser(context))
{
@* Whether its settings are complete does not matter while nobody can use the tool: *@
<MudTooltip Text="@T("You switched off this tool. It appears in no tool selection, and the AI cannot use it.")">
<MudTooltip Text="@TA("You switched off this tool. It appears in no tool selection, and the AI cannot use it.")">
<MudIcon Icon="@AppIcons.HIDDEN" Color="Color.Default" />
</MudTooltip>
}
else if (!context.IsActive)
{
<MudTooltip Text="@T("This tool has been disabled by your organization.")">
<MudTooltip Text="@TA("This tool has been disabled by your organization.")">
<MudIcon Icon="@AppIcons.LOCK" Color="Color.Error" />
</MudTooltip>
}
@@ -57,11 +57,11 @@ public partial class SettingsPanelTools : SettingsPanelBase
private string GetSwitchTooltip(ToolCatalogItem item)
{
if (IsSwitchLocked(item))
return this.T("This tool has been disabled by your organization.");
return this.TA("This tool has been disabled by your organization.");
return IsSwitchedOffByUser(item)
? this.T("Switch this tool on to use it again.")
: this.T("Switch this tool off if you never need it. It then appears in no tool selection, and the AI cannot use it.");
? this.TA("Switch this tool on to use it again.")
: this.TA("Switch this tool off if you never need it. It then appears in no tool selection, and the AI cannot use it.");
}
private async Task SwitchToolOnOrOff(ToolCatalogItem item, bool isOn)
@@ -11,7 +11,7 @@
@T("Configured Transcription Providers")
</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section.")
@TA("With the support of transcription models, MindWork AI Studio can convert human speech into text. This is useful, for example, when you need to dictate text. You can choose from dedicated transcription models, but not multimodal LLMs (large language models) that can handle both speech and text. The configuration of multimodal models is done in the 'Configure providers' section.")
</MudJustifiedText>
<MudTable Items="@this.SettingsManager.GetAllTranscriptionProviders()" Hover="@true" GroupBy="@GROUP_CONFIG" Class="border-dashed border rounded-lg">
@@ -43,25 +43,25 @@
<MudStack Row="true" Class="mb-2 mt-2" Spacing="1" Wrap="Wrap.Wrap">
@if (this.SettingsManager.IsProviderProhibited(context.UsedLLMProvider))
{
<MudTooltip Text="@T("The use of this transcription provider has been prohibited by your organization.")">
<MudTooltip Text="@TA("The use of this transcription provider has been prohibited by your organization.")">
<MudIconButton Color="Color.Error" Icon="@AppIcons.BLOCKED" Disabled="true"/>
</MudTooltip>
}
@if (context.IsTrustedForDataSourceSecurityChecks(this.SettingsManager))
{
<MudTooltip Text="@(context.IsSelfHosted ? T("This self-hosted transcription provider is trusted for data source security checks.") : T("This transcription provider is trusted by your organization for data source security checks."))">
<MudTooltip Text="@(context.IsSelfHosted ? T("This self-hosted transcription provider is trusted for data source security checks.") : TA("This transcription provider is trusted by your organization for data source security checks."))">
<MudIconButton Color="Color.Success" Icon="@AppIcons.TRUSTED" Disabled="true"/>
</MudTooltip>
}
@if (context.IsEnterpriseConfiguration && !context.AllowUserProvidedAPIKey)
{
<MudTooltip Text="@T("This transcription provider is managed by your organization.")">
<MudTooltip Text="@TA("This transcription provider is managed by your organization.")">
<MudIconButton Color="Color.Info" Icon="@AppIcons.ORGANIZATION" Disabled="true"/>
</MudTooltip>
}
else if (context.IsEnterpriseConfiguration && context.AllowUserProvidedAPIKey)
{
<MudTooltip Text="@T("This transcription provider is managed by your organization. You can set your own API key.")">
<MudTooltip Text="@TA("This transcription provider is managed by your organization. You can set your own API key.")">
<MudIconButton Color="Color.Info" Icon="@AppIcons.KEY" OnClick="@(() => this.EditTranscriptionProvider(context))"/>
</MudTooltip>
}
@@ -148,7 +148,7 @@ public partial class SettingsPanelTranscription : SettingsPanelProviderBase
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Are you sure you want to delete the transcription provider '{0}'?"), provider.Name) },
{ x => x.Message, string.Format(TA("Are you sure you want to delete the transcription provider '{0}'?"), provider.Name) },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Transcription Provider"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -25,6 +25,8 @@ public partial class TokenizerHint : ComponentBase
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(TokenizerHint).Namespace, nameof(TokenizerHint));
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(TokenizerHint).Namespace, nameof(TokenizerHint));
/// <summary>
/// Which provider the model is served by.
/// </summary>
@@ -61,7 +63,7 @@ public partial class TokenizerHint : ComponentBase
private static string Describe(TokenizerRef tokenizer) => tokenizer.Kind switch
{
TokenizerKind.HUGGING_FACE => string.Format(TB("This model uses the tokenizer of {0}. Download its tokenizer.json file and select it below to count exactly instead of estimating."), tokenizer.Id),
TokenizerKind.HUGGING_FACE => string.Format(TA("This model uses the tokenizer of {0}. Download its tokenizer.json file and select it below to count exactly instead of estimating."), tokenizer.Id),
TokenizerKind.TIKTOKEN => string.Format(TB("This model uses OpenAI's {0} encoding, which does not come as a tokenizer.json file. AI Studio therefore estimates the token count with its built-in tokenizer."), tokenizer.Id),
TokenizerKind.PROVIDER_API => string.Format(TB("The vendor of this model publishes no tokenizer file and counts through their API instead ({0}). AI Studio therefore estimates the token count with its built-in tokenizer."), tokenizer.Id),
@@ -6,5 +6,5 @@
{
<ConfigurationOption OptionDescription="@T("Show tool selection in this assistant?")" LabelOn="@T("Tool selection is visible")" LabelOff="@T("Tool selection is hidden")" State="@(() => this.SettingsManager.IsToolSelectionVisible(this.Component))" StateUpdate="@(value => this.SettingsManager.SetToolSelectionVisibility(this.Component, value))" />
}
<ConfigurationMultiSelect TData="string" OptionDescription="@this.OptionTitle" SelectedValues="@this.GetSelectedValues" Data="@this.availableTools" SelectionUpdate="@this.UpdateSelection" OptionHelp="@this.OptionHelp" Disabled="@(() => this.AreDefaultToolsDisabled)" IsItemLocked="@this.IsToolDisabled" EmptySelectionText="@T("No tools selected.")" SingleSelectionText="@T("You have selected 1 tool.")" MultipleSelectionText="@T("You have selected {0} tools.")" />
<ConfigurationMultiSelect TData="string" OptionDescription="@this.OptionTitle" SelectedValues="@this.GetSelectedValues" Data="@this.availableTools" SelectionUpdate="@this.UpdateSelection" OptionHelp="@this.OptionHelp" Disabled="@(() => this.AreDefaultToolsDisabled)" IsItemLocked="@this.IsToolDisabled" EmptySelectionText="@T("No tools selected.")" SingleSelectionText="@TA("You have selected 1 tool.")" MultipleSelectionText="@TA("You have selected {0} tools.")" />
}
@@ -21,8 +21,8 @@ public partial class ToolDefaultsConfiguration : MSGComponentBase
private string OptionTitle => this.Component is AIStudio.Tools.Components.CHAT ? this.T("Default tools for chat") : this.T("Default tools for this assistant");
private string OptionHelp => this.Component is AIStudio.Tools.Components.CHAT
? this.T("Choose which tools should be preselected for new chats.")
: this.T("Choose which tools should be preselected for new runs of this assistant.");
? this.TA("Choose which tools should be preselected for new chats.")
: this.TA("Choose which tools should be preselected for new runs of this assistant.");
/// <summary>
/// Whether preselecting tools is pointless right now.
@@ -27,7 +27,7 @@
else if (this.Disabled)
{
<MudAlert Dense="@true" Severity="Severity.Info" Variant="Variant.Outlined" Class="mb-2">
@T("Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished.")
@TA("Tool changes are locked while a response is running. Your current selection is shown below and applies again from the next message once the run is finished.")
</MudAlert>
}
else if (this.catalog.Count == 0)
@@ -76,7 +76,7 @@
}
</div>
<MudJustifiedText Typo="Typo.caption" HtmlTag="div" Class="mt-2 ml-2">
@T("You can switch off tools you never need in the app settings, under Tool Settings. They then no longer appear here.")
@TA("You can switch off tools you never need in the app settings, under Tool Settings. They then no longer appear here.")
</MudJustifiedText>
</MudCollapse>
}
@@ -122,7 +122,7 @@
<MudIcon Icon="@item.Icon" Color="Color.Info" />
@if (!item.IsActive)
{
<MudTooltip Text="@T("This tool has been disabled by your organization.")">
<MudTooltip Text="@TA("This tool has been disabled by your organization.")">
<MudIcon Icon="@AppIcons.LOCK" Color="Color.Error" Size="Size.Small" />
</MudTooltip>
}
@@ -209,10 +209,10 @@ public partial class ToolSelection : MSGComponentBase
{
var warnings = new List<string>(4);
if (!item.ConfigurationState.IsConfigured)
warnings.Add(string.IsNullOrWhiteSpace(item.ConfigurationState.Message) ? T("Required settings are missing. Configure this tool before enabling it.") : item.ConfigurationState.Message);
warnings.Add(string.IsNullOrWhiteSpace(item.ConfigurationState.Message) ? TA("Required settings are missing. Configure this tool before enabling it.") : item.ConfigurationState.Message);
if (!item.IsActive)
warnings.Add(T("This tool has been disabled by your organization."));
warnings.Add(TA("This tool has been disabled by your organization."));
var providerConfidenceHint = this.GetProviderConfidenceHint(item);
if (!string.IsNullOrWhiteSpace(providerConfidenceHint))
@@ -8,7 +8,7 @@
else
{
<MudText Typo="Typo.body1" Class="mb-3">
@T("Your organization has set up AI providers for you. To use them, AI Studio needs your personal API key. Please enter it below.")
@TA("Your organization has set up AI providers for you. To use them, AI Studio needs your personal API key. Please enter it below.")
</MudText>
}
@@ -40,7 +40,7 @@ public partial class UserAPIKeyPrompt : MSGComponentBase
private string issue = string.Empty;
private bool isBusy;
private string Title => string.IsNullOrWhiteSpace(this.Group.Prompt?.Title) ? T("Your API key is missing") : this.Group.Prompt.Title;
private string Title => string.IsNullOrWhiteSpace(this.Group.Prompt?.Title) ? TA("Your API key is missing") : this.Group.Prompt.Title;
private async Task StoreAPIKeyAsync()
{
@@ -55,7 +55,7 @@ public partial class UserAPIKeyPrompt : MSGComponentBase
var result = await PendingUserAPIKeys.StoreAsync(this.Group, enteredAPIKey, this.RustService, this.Logger);
if (result.Check is UserAPIKeyCheckOutcome.REJECTED)
{
this.issue = T("The provider did not accept this API key. Please check the key and try again.");
this.issue = TA("The provider did not accept this API key. Please check the key and try again.");
return;
}
@@ -71,9 +71,9 @@ public partial class UserAPIKeyPrompt : MSGComponentBase
this.apiKey = string.Empty;
if (result.Check is UserAPIKeyCheckOutcome.ACCEPTED)
await this.MessageBus.SendSuccess(new DataSuccessMessage(AppIcons.KEY, T("AI Studio checked your API key and stored it. You can now use the AI providers of your organization.")));
await this.MessageBus.SendSuccess(new DataSuccessMessage(AppIcons.KEY, TA("AI Studio checked your API key and stored it. You can now use the AI providers of your organization.")));
else
await this.MessageBus.SendWarning(new DataWarningMessage(AppIcons.KEY, T("AI Studio stored your API key but could not check it. If the AI providers of your organization do not work, please correct the key in the settings.")));
await this.MessageBus.SendWarning(new DataWarningMessage(AppIcons.KEY, TA("AI Studio stored your API key but could not check it. If the AI providers of your organization do not work, please correct the key in the settings.")));
// Tell everyone that the providers are usable now. The home page removes this panel once no provider waits any longer:
await this.SendMessage<bool>(Event.CONFIGURATION_CHANGED);
@@ -1,11 +1,11 @@
@inherits MSGComponentBase
<MudText Typo="Typo.body1" Class="mb-3" Style="text-align: justify; hyphens: auto;">
@T("Curious about the vision for MindWork AI Studio and what the future holds? We're here to address just that. Remember, this is a free, open-source project, meaning we can't guarantee when or if this vision will be fully realized. Our aim is to share our vision with you to help you decide whether this app is right for you.")
@TA("Curious about the vision for MindWork AI Studio and what the future holds? We're here to address just that. Remember, this is a free, open-source project, meaning we can't guarantee when or if this vision will be fully realized. Our aim is to share our vision with you to help you decide whether this app is right for you.")
</MudText>
<MudText Typo="Typo.body1" Class="mb-3" Style="text-align: justify; hyphens: auto;">
@T("So, where are we headed, and how could the app evolve in the coming months and years? The following list outlines our ideas, though not in order of priority:")
</MudText>
<MudTextList Icon="@AppIcons.CHECKLIST_ITEM" Clickable="@true" Items="@this.itemsVision" Class="mb-3"/>
<MudText Typo="Typo.body1" Class="mb-3" Style="text-align: justify; hyphens: auto;">
@T("We hope this vision excites you as much as it excites us. Together, let's build a powerful and flexible AI toolkit to support all your creative, professional, and everyday needs with MindWork AI Studio.")
@TA("We hope this vision excites you as much as it excites us. Together, let's build a powerful and flexible AI toolkit to support all your creative, professional, and everyday needs with MindWork AI Studio.")
</MudText>
@@ -18,17 +18,17 @@ public partial class Vision : MSGComponentBase
{
this.itemsVision =
[
new(T("Meet your needs"), T("Whatever your job or task is, MindWork AI Studio aims to meet your needs: whether you're a project manager, scientist, artist, author, software developer, or game developer.")),
new(T("Democratization of AI"), T("We want to contribute to the democratization of AI. MindWork AI Studio runs even on low-cost hardware, including computers around 100 EUR such as Raspberry Pi. This makes the app and its full feature set accessible to people and families with limited budgets. You can start with local LLMs or use affordable cloud models. MindWork AI Studio itself is available free of charge.")),
new(T("Integrating your data"), T("You'll be able to integrate your data into AI Studio, like your PDF or Office files, or your Markdown notes.")),
new(TA("Meet your needs"), TA("Whatever your job or task is, MindWork AI Studio aims to meet your needs: whether you're a project manager, scientist, artist, author, software developer, or game developer.")),
new(T("Democratization of AI"), TA("We want to contribute to the democratization of AI. MindWork AI Studio runs even on low-cost hardware, including computers around 100 EUR such as Raspberry Pi. This makes the app and its full feature set accessible to people and families with limited budgets. You can start with local LLMs or use affordable cloud models. MindWork AI Studio itself is available free of charge.")),
new(TA("Integrating your data"), TA("You'll be able to integrate your data into AI Studio, like your PDF or Office files, or your Markdown notes.")),
new(T("Integration of enterprise data"), T("It will soon be possible to integrate data from the corporate network using a specified interface (External Retrieval Interface, ERI for short). This will likely require development work by the organization in question.")),
new(T("Useful assistants"), T("We'll develop more assistants for everyday tasks.")),
new(T("Writing mode"), T("We're integrating a writing mode to help you create extensive works, like comprehensive project proposals, tenders, or your next fantasy novel.")),
new(T("Specific requirements"), T("Want an assistant that suits your specific needs? We aim to offer a plugin architecture so organizations and enthusiasts can implement such ideas.")),
new(T("Voice control"), T("You'll interact with the AI systems using your voice. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation.")),
new(T("Content creation"), T("There will be an interface for AI Studio to create content in other apps. You could, for example, create blog posts directly on the target platform or add entries to an internal knowledge management tool. This requires development work by the tool developers.")),
new(T("Email monitoring"), T("You can connect your email inboxes with AI Studio. The AI will read your emails and notify you of important events. You'll also be able to access knowledge from your emails in your chats.")),
new(T("Browser usage"), T("We're working on offering AI Studio features in your browser via a plugin, allowing, e.g., for spell-checking or text rewriting directly in the browser.")),
new(T("Writing mode"), TA("We're integrating a writing mode to help you create extensive works, like comprehensive project proposals, tenders, or your next fantasy novel.")),
new(T("Specific requirements"), TA("Want an assistant that suits your specific needs? We aim to offer a plugin architecture so organizations and enthusiasts can implement such ideas.")),
new(T("Voice control"), TA("You'll interact with the AI systems using your voice. To achieve this, we want to integrate voice input (speech-to-text) and output (text-to-speech). However, later on, it should also have a natural conversation flow, i.e., seamless conversation.")),
new(T("Content creation"), TA("There will be an interface for AI Studio to create content in other apps. You could, for example, create blog posts directly on the target platform or add entries to an internal knowledge management tool. This requires development work by the tool developers.")),
new(T("Email monitoring"), TA("You can connect your email inboxes with AI Studio. The AI will read your emails and notify you of important events. You'll also be able to access knowledge from your emails in your chats.")),
new(T("Browser usage"), TA("We're working on offering AI Studio features in your browser via a plugin, allowing, e.g., for spell-checking or text rewriting directly in the browser.")),
];
}
@@ -170,7 +170,7 @@ public partial class VoiceRecorder : MSGComponentBase
? T("Transcription in progress...")
: this.isRecording
? T("Stop recording and start transcription")
: T("Start recording your voice for a transcription");
: TA("Start recording your voice for a transcription");
private async Task OnRecordingToggled(bool toggled)
{
@@ -137,7 +137,7 @@ public partial class Workspaces : MSGComponentBase
workspace.WorkspaceId != excludedWorkspaceId &&
string.Equals(WorkspaceBehaviour.NormalizeWorkspaceName(workspace.Name), normalizedWorkspaceName, StringComparison.OrdinalIgnoreCase));
return nameExists ? T("There is already a workspace with this name. Please choose a different name.") : null;
return nameExists ? TA("There is already a workspace with this name. Please choose a different name.") : null;
};
}
@@ -716,7 +716,7 @@ public partial class Workspaces : MSGComponentBase
{
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, T("Are you sure you want to load another chat? All unsaved changes will be lost.") },
{ x => x.Message, TA("Are you sure you want to load another chat? All unsaved changes will be lost.") },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Load Chat"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -800,13 +800,13 @@ public partial class Workspaces : MSGComponentBase
var dialogParameters = new DialogParameters<SingleInputDialog>
{
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your chat '{0}':"), chat.Name) },
{ x => x.Message, string.Format(TA("Please enter a new or edit the name for your chat '{0}':"), chat.Name) },
{ x => x.InputHeaderText, T("Chat Name") },
{ x => x.UserInput, chat.Name },
{ x => x.ConfirmText, T("Rename") },
{ x => x.ConfirmColor, Color.Info },
{ x => x.AllowEmptyInput, false },
{ x => x.EmptyInputErrorMessage, T("Please enter a chat name.") },
{ x => x.EmptyInputErrorMessage, TA("Please enter a chat name.") },
};
var dialogReference = await this.DialogService.ShowAsync<SingleInputDialog>(T("Rename Chat"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -860,8 +860,8 @@ public partial class Workspaces : MSGComponentBase
{
x => x.Message, isCopyOfOpenChat switch
{
true => T("Do you want to copy this chat? Your unsaved changes move into the copy, and the original chat keeps the state it was last saved with."),
false => T("Do you want to copy this chat? The copy is opened afterwards, so all unsaved changes of the chat you have open right now will be lost."),
true => TA("Do you want to copy this chat? Your unsaved changes move into the copy, and the original chat keeps the state it was last saved with."),
false => TA("Do you want to copy this chat? The copy is opened afterwards, so all unsaved changes of the chat you have open right now will be lost."),
}
},
};
@@ -890,13 +890,13 @@ public partial class Workspaces : MSGComponentBase
var workspaceName = await WorkspaceBehaviour.LoadWorkspaceNameAsync(workspaceId);
var dialogParameters = new DialogParameters<SingleInputDialog>
{
{ x => x.Message, string.Format(T("Please enter a new or edit the name for your workspace '{0}':"), workspaceName) },
{ x => x.Message, string.Format(TA("Please enter a new or edit the name for your workspace '{0}':"), workspaceName) },
{ x => x.InputHeaderText, T("Workspace Name") },
{ x => x.UserInput, workspaceName },
{ x => x.ConfirmText, T("Rename") },
{ x => x.ConfirmColor, Color.Info },
{ x => x.AllowEmptyInput, false },
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
{ x => x.EmptyInputErrorMessage, TA("Please enter a workspace name.") },
{ x => x.AdditionalValidation, await this.CreateWorkspaceNameValidationAsync(workspaceId, workspaceName) },
};
@@ -917,13 +917,13 @@ public partial class Workspaces : MSGComponentBase
{
var dialogParameters = new DialogParameters<SingleInputDialog>
{
{ x => x.Message, T("Please name your workspace:") },
{ x => x.Message, TA("Please name your workspace:") },
{ x => x.InputHeaderText, T("Workspace Name") },
{ x => x.UserInput, string.Empty },
{ x => x.ConfirmText, T("Add workspace") },
{ x => x.ConfirmColor, Color.Info },
{ x => x.AllowEmptyInput, false },
{ x => x.EmptyInputErrorMessage, T("Please enter a workspace name.") },
{ x => x.EmptyInputErrorMessage, TA("Please enter a workspace name.") },
{ x => x.AdditionalValidation, await this.CreateWorkspaceNameValidationAsync() },
};
@@ -963,7 +963,7 @@ public partial class Workspaces : MSGComponentBase
var chatCount = Directory.EnumerateDirectories(workspacePath).Count();
var dialogParameters = new DialogParameters<ConfirmDialog>
{
{ x => x.Message, string.Format(T("Are you sure you want to delete the workspace '{0}'? This will also delete {1} chat(s) in this workspace."), workspaceName, chatCount) },
{ x => x.Message, string.Format(TA("Are you sure you want to delete the workspace '{0}'? This will also delete {1} chat(s) in this workspace."), workspaceName, chatCount) },
};
var dialogReference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Delete Workspace"), dialogParameters, DialogOptions.FULLSCREEN);
@@ -1007,7 +1007,7 @@ public partial class Workspaces : MSGComponentBase
return chatIds;
}
private Task WarnAboutBusyWorkspaceAsync(string workspaceName) => MessageBus.INSTANCE.SendWarning(new(AppIcons.DELETE, string.Format(T("The workspace '{0}' cannot be deleted while one of its chats is being answered or transcribed. Please try again once that is finished."), workspaceName)));
private Task WarnAboutBusyWorkspaceAsync(string workspaceName) => MessageBus.INSTANCE.SendWarning(new(AppIcons.DELETE, string.Format(TA("The workspace '{0}' cannot be deleted while one of its chats is being answered or transcribed. Please try again once that is finished."), workspaceName)));
/// <summary>
/// Moves the given chat into another workspace and shows it there in the tree.
@@ -1047,7 +1047,7 @@ public partial class Workspaces : MSGComponentBase
var dialogParameters = new DialogParameters<WorkspaceSelectionDialog>
{
{ x => x.Message, T("Please select the workspace where you want to move the chat to.") },
{ x => x.Message, TA("Please select the workspace where you want to move the chat to.") },
{ x => x.SelectedWorkspace, chat.WorkspaceId },
{ x => x.ConfirmText, T("Move chat") },
};