Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
var availablePreviewFeatures = ConfigurationSelectDataFactory.GetPreviewFeaturesData(this.SettingsManager).ToList();
if (availablePreviewFeatures.Count > 0)
{
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@(() => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures.Where(x => !x.IsReleased()).ToHashSet())" Data="@availablePreviewFeatures" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedValue)" OptionHelp="@T("Which preview features would you like to enable?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.IsLocked"/>
<ConfigurationMultiSelect OptionDescription="@T("Select preview features")" SelectedValues="@this.GetSelectedPreviewFeatures" Data="@availablePreviewFeatures" SelectionUpdate="@this.UpdateEnabledPreviewFeatures" OptionHelp="@T("Which preview features would you like to enable?")" IsLocked="() => ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.IsLocked"/>
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,30 @@ private IEnumerable<ConfigurationSelectData<string>> GetFilteredTranscriptionPro
private void UpdatePreviewFeatures(PreviewVisibility previewVisibility)
{
this.SettingsManager.ConfigurationData.App.PreviewVisibility = previewVisibility;
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = previewVisibility.FilterPreviewFeatures(this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures);
var filtered = previewVisibility.FilterPreviewFeatures(this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures);
filtered.UnionWith(this.GetManagedPreviewFeatures());
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = filtered;
}

private HashSet<PreviewFeatures> GetManagedPreviewFeatures()
{
if (ManagedConfiguration.TryGet(x => x.App, x => x.EnabledPreviewFeatures, out var meta) && meta.HasManagedValue)
return meta.ManagedValue.Where(x => !x.IsReleased()).ToHashSet();

return [];
}

private HashSet<PreviewFeatures> GetSelectedPreviewFeatures()
{
var enabled = this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures.Where(x => !x.IsReleased()).ToHashSet();
enabled.UnionWith(this.GetManagedPreviewFeatures());
return enabled;
}

private void UpdateEnabledPreviewFeatures(HashSet<PreviewFeatures> selectedFeatures)
{
selectedFeatures.UnionWith(this.GetManagedPreviewFeatures());
this.SettingsManager.ConfigurationData.App.EnabledPreviewFeatures = selectedFeatures;
}

private async Task UpdateLangBehaviour(LangBehavior behavior)
Expand All @@ -41,4 +64,4 @@ private async Task UpdateManuallySelectedLanguage(Guid pluginId)
this.SettingsManager.ConfigurationData.App.LanguagePluginId = pluginId;
await this.MessageBus.SendMessage<bool>(this, Event.PLUGINS_RELOADED);
}
}
}
46 changes: 45 additions & 1 deletion app/MindWork AI Studio/Settings/ConfigMeta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ public ConfigMeta(Expression<Func<Data, TClass>> configSelection, Expression<Fun
/// </summary>
public required TValue Default { get; init; }

/// <summary>
/// Indicates whether a managed value is available.
/// </summary>
public bool HasManagedValue { get; private set; }

/// <summary>
/// The managed value provided by a configuration plugin.
/// </summary>
public TValue ManagedValue { get; private set; } = default!;

/// <summary>
/// The ID of the plugin that provided the managed value.
/// </summary>
public Guid ManagedValueByConfigPluginId { get; private set; }

/// <summary>
/// Locks the configuration state, indicating that it is managed by a specific plugin.
/// </summary>
Expand All @@ -62,6 +77,35 @@ public void ResetManagedState()
this.MangedByConfigPluginId = Guid.Empty;
this.Reset();
}

/// <summary>
/// Unlocks the configuration state without changing the current value.
/// </summary>
public void UnlockManagedState()
{
this.IsLocked = false;
this.MangedByConfigPluginId = Guid.Empty;
}

/// <summary>
/// Stores a managed value provided by a configuration plugin.
/// </summary>
public void SetManagedValue(TValue value, Guid pluginId)
{
this.ManagedValue = value;
this.ManagedValueByConfigPluginId = pluginId;
this.HasManagedValue = true;
}

/// <summary>
/// Clears the managed value without changing the current value.
/// </summary>
public void ClearManagedValue()
{
this.ManagedValue = default!;
this.ManagedValueByConfigPluginId = Guid.Empty;
this.HasManagedValue = false;
}

/// <summary>
/// Resets the configuration property to its default value.
Expand All @@ -85,4 +129,4 @@ public void SetValue(TValue value)
if (memberExpression.Member is System.Reflection.PropertyInfo propertyInfo)
propertyInfo.SetValue(configInstance, value);
}
}
}
86 changes: 85 additions & 1 deletion app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,90 @@ configuredLuaList.Type is LuaValueType.Table &&

return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
}

/// <summary>
/// Attempts to process the configuration settings from a Lua table for enum set types.
/// The configured values are merged into the existing set and the setting is left unlocked
/// so users can add additional values.
/// </summary>
/// <param name="configPluginId">The ID of the related configuration plugin.</param>
/// <param name="settings">The Lua table containing the settings to process.</param>
/// <param name="configSelection">The expression to select the configuration class.</param>
/// <param name="propertyExpression">The expression to select the property within the configuration class.</param>
/// <param name="dryRun">When true, the method will not apply any changes but only check if the configuration can be read.</param>
/// <typeparam name="TClass">The type of the configuration class.</typeparam>
/// <typeparam name="TValue">The type of the property within the configuration class. It is also the type of the set
/// elements, which must be an enum.</typeparam>
/// <returns>True when the configuration was successfully processed, otherwise false.</returns>
public static bool TryProcessConfigurationAdditive<TClass, TValue>(
Expression<Func<Data, TClass>> configSelection,
Expression<Func<TClass, ISet<TValue>>> propertyExpression,
Guid configPluginId,
LuaTable settings,
bool dryRun)
where TValue : Enum
{
//
// Handle configured enum sets (additive merge)
//

// Check if that configuration was registered:
if (!TryGet(configSelection, propertyExpression, out var configMeta))
return false;

var successful = false;
var configuredValue = new HashSet<TValue>();

// Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
configuredLuaList.Type is LuaValueType.Table &&
configuredLuaList.TryRead<LuaTable>(out var valueTable))
{
// Determine the length of the Lua table and prepare a set to hold the parsed values:
var len = valueTable.ArrayLength;
var set = new HashSet<TValue>(len);

// Iterate over each entry in the Lua table:
for (var index = 1; index <= len; index++)
{
// Retrieve the Lua value at the current index:
var value = valueTable[index];

// Step 2 -- try to read the Lua value as a string:
if (value.Type is LuaValueType.String && value.TryRead<string>(out var configuredLuaValueText))
{
// Step 3 -- try to parse the string as the target type:
if (Enum.TryParse(typeof(TValue), configuredLuaValueText, true, out var configuredEnum))
set.Add((TValue)configuredEnum);
}
}

configuredValue = set;
successful = true;
}

if (dryRun)
return successful;

if (successful)
{
var configInstance = configSelection.Compile().Invoke(SETTINGS_MANAGER.ConfigurationData);
var currentValue = propertyExpression.Compile().Invoke(configInstance);
var merged = new HashSet<TValue>(currentValue);
merged.UnionWith(configuredValue);
configMeta.SetValue(merged);
configMeta.SetManagedValue(new HashSet<TValue>(configuredValue), configPluginId);
}
else if (configMeta.HasManagedValue && configMeta.ManagedValueByConfigPluginId == configPluginId)
{
configMeta.ClearManagedValue();
}

if (configMeta.IsLocked && configMeta.MangedByConfigPluginId == configPluginId)
configMeta.UnlockManagedState();

return successful;
}

/// <summary>
/// Attempts to process the configuration settings from a Lua table for string set types.
Expand Down Expand Up @@ -773,4 +857,4 @@ private static bool HandleParsedValue<TClass, TValue>(

return successful;
}
}
}
28 changes: 27 additions & 1 deletion app/MindWork AI Studio/Settings/ManagedConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace AIStudio.Settings;
public static partial class ManagedConfiguration
{
private static readonly ConcurrentDictionary<string, IConfig> METADATA = new();
private static readonly SettingsManager SETTINGS_MANAGER = Program.SERVICE_PROVIDER.GetRequiredService<SettingsManager>();

/// <summary>
/// Attempts to retrieve the configuration metadata for a given configuration selection and
Expand Down Expand Up @@ -353,6 +354,31 @@ public static bool IsConfigurationLeftOver<TClass, TValue>(
return false;
}

/// <summary>
/// Checks if a managed value is left over from a configuration plugin that is no longer available.
/// If so, it clears the managed value and returns true.
/// </summary>
public static bool IsManagedValueLeftOver<TClass, TValue>(
Expression<Func<Data, TClass>> configSelection,
Expression<Func<TClass, ISet<TValue>>> propertyExpression,
IEnumerable<IAvailablePlugin> availablePlugins)
{
if (!TryGet(configSelection, propertyExpression, out var configMeta))
return false;

if (!configMeta.HasManagedValue || configMeta.ManagedValueByConfigPluginId == Guid.Empty)
return false;

var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.ManagedValueByConfigPluginId);
if (plugin is null)
{
configMeta.ClearManagedValue();
return true;
}

return false;
}

public static bool IsConfigurationLeftOver<TClass>(
Expression<Func<Data, TClass>> configSelection,
Expression<Func<TClass, IDictionary<string, string>>> propertyExpression,
Expand Down Expand Up @@ -387,4 +413,4 @@ private static string Path<TClass, TValue>(Expression<Func<Data, TClass>> config
var configPath = $"{configName}.{className}.{propertyName}";
return configPath;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ private bool TryProcessConfiguration(bool dryRun, out string message)
// Config: preview features visibility
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.PreviewVisibility, this.Id, settingsTable, dryRun);

// Config: enabled preview features
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.EnabledPreviewFeatures, this.Id, settingsTable, dryRun);
// Config: enabled preview features (additive; users can enable additional features)
ManagedConfiguration.TryProcessConfigurationAdditive(x => x.App, x => x.EnabledPreviewFeatures, this.Id, settingsTable, dryRun);

// Config: hide some assistants?
ManagedConfiguration.TryProcessConfiguration(x => x.App, x => x.HiddenAssistants, this.Id, settingsTable, dryRun);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ public static async Task LoadAll(CancellationToken cancellationToken = default)
// Check for enabled preview features:
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.EnabledPreviewFeatures, AVAILABLE_PLUGINS))
wasConfigurationChanged = true;
if(ManagedConfiguration.IsManagedValueLeftOver(x => x.App, x => x.EnabledPreviewFeatures, AVAILABLE_PLUGINS))
wasConfigurationChanged = true;

// Check for the transcription provider:
if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.UseTranscriptionProvider, AVAILABLE_PLUGINS))
Expand Down