mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-03-29 21:31:39 +00:00
WIP: Multiselection support
This commit is contained in:
parent
a182cc438a
commit
133be5b325
@ -87,22 +87,42 @@
|
|||||||
case AssistantComponentType.DROPDOWN:
|
case AssistantComponentType.DROPDOWN:
|
||||||
if (component is AssistantDropdown assistantDropdown)
|
if (component is AssistantDropdown assistantDropdown)
|
||||||
{
|
{
|
||||||
<DynamicAssistantDropdown Items="@assistantDropdown.Items"
|
if (assistantDropdown.IsMultiselect)
|
||||||
@bind-Value="@this.dropdownFields[assistantDropdown.Name]"
|
{
|
||||||
Default="@assistantDropdown.Default"
|
<DynamicAssistantDropdown Items="@assistantDropdown.Items"
|
||||||
Label="@assistantDropdown.Label"
|
SelectedValues="@this.multiselectDropdownFields[assistantDropdown.Name]"
|
||||||
HelperText="@assistantDropdown.HelperText"
|
SelectedValuesChanged="@this.CreateMultiselectDropdownChangedCallback(assistantDropdown.Name)"
|
||||||
OpenIcon="@AssistantComponentPropHelper.GetIconSvg(assistantDropdown.OpenIcon)"
|
Default="@assistantDropdown.Default"
|
||||||
CloseIcon="@AssistantComponentPropHelper.GetIconSvg(assistantDropdown.CloseIcon)"
|
Label="@assistantDropdown.Label"
|
||||||
IconColor="@AssistantComponentPropHelper.GetColor(assistantDropdown.IconColor, Color.Default)"
|
HelperText="@assistantDropdown.HelperText"
|
||||||
IconPosition="@AssistantComponentPropHelper.GetAdornment(assistantDropdown.IconPositon, Adornment.End)"
|
OpenIcon="@AssistantComponentPropHelper.GetIconSvg(assistantDropdown.OpenIcon)"
|
||||||
Variant="@AssistantComponentPropHelper.GetVariant(assistantDropdown.Variant, Variant.Outlined)"
|
CloseIcon="@AssistantComponentPropHelper.GetIconSvg(assistantDropdown.CloseIcon)"
|
||||||
IsMultiselect="@assistantDropdown.IsMultiselect"
|
IconColor="@AssistantComponentPropHelper.GetColor(assistantDropdown.IconColor, Color.Default)"
|
||||||
HasSelectAll="@assistantDropdown.HasSelectAll"
|
IconPosition="@AssistantComponentPropHelper.GetAdornment(assistantDropdown.IconPositon, Adornment.End)"
|
||||||
SelectAllText="@assistantDropdown.SelectAllText"
|
Variant="@AssistantComponentPropHelper.GetVariant(assistantDropdown.Variant, Variant.Outlined)"
|
||||||
Class="@assistantDropdown.Class"
|
IsMultiselect="@true"
|
||||||
Style="@this.GetOptionalStyle(assistantDropdown.Style)"
|
HasSelectAll="@assistantDropdown.HasSelectAll"
|
||||||
/>
|
SelectAllText="@assistantDropdown.SelectAllText"
|
||||||
|
Class="@assistantDropdown.Class"
|
||||||
|
Style="@this.GetOptionalStyle(assistantDropdown.Style)" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<DynamicAssistantDropdown Items="@assistantDropdown.Items"
|
||||||
|
@bind-Value="@this.dropdownFields[assistantDropdown.Name]"
|
||||||
|
Default="@assistantDropdown.Default"
|
||||||
|
Label="@assistantDropdown.Label"
|
||||||
|
HelperText="@assistantDropdown.HelperText"
|
||||||
|
OpenIcon="@AssistantComponentPropHelper.GetIconSvg(assistantDropdown.OpenIcon)"
|
||||||
|
CloseIcon="@AssistantComponentPropHelper.GetIconSvg(assistantDropdown.CloseIcon)"
|
||||||
|
IconColor="@AssistantComponentPropHelper.GetColor(assistantDropdown.IconColor, Color.Default)"
|
||||||
|
IconPosition="@AssistantComponentPropHelper.GetAdornment(assistantDropdown.IconPositon, Adornment.End)"
|
||||||
|
Variant="@AssistantComponentPropHelper.GetVariant(assistantDropdown.Variant, Variant.Outlined)"
|
||||||
|
HasSelectAll="@assistantDropdown.HasSelectAll"
|
||||||
|
SelectAllText="@assistantDropdown.SelectAllText"
|
||||||
|
Class="@assistantDropdown.Class"
|
||||||
|
Style="@this.GetOptionalStyle(assistantDropdown.Style)" />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case AssistantComponentType.BUTTON:
|
case AssistantComponentType.BUTTON:
|
||||||
|
|||||||
@ -43,6 +43,7 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
|
|
||||||
private readonly Dictionary<string, string> inputFields = new();
|
private readonly Dictionary<string, string> inputFields = new();
|
||||||
private readonly Dictionary<string, string> dropdownFields = new();
|
private readonly Dictionary<string, string> dropdownFields = new();
|
||||||
|
private readonly Dictionary<string, HashSet<string>> multiselectDropdownFields = new();
|
||||||
private readonly Dictionary<string, bool> switchFields = new();
|
private readonly Dictionary<string, bool> switchFields = new();
|
||||||
private readonly Dictionary<string, WebContentState> webContentFields = new();
|
private readonly Dictionary<string, WebContentState> webContentFields = new();
|
||||||
private readonly Dictionary<string, FileContentState> fileContentFields = new();
|
private readonly Dictionary<string, FileContentState> fileContentFields = new();
|
||||||
@ -218,6 +219,8 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
fields[entry.Key] = entry.Value ?? string.Empty;
|
fields[entry.Key] = entry.Value ?? string.Empty;
|
||||||
foreach (var entry in this.dropdownFields)
|
foreach (var entry in this.dropdownFields)
|
||||||
fields[entry.Key] = entry.Value ?? string.Empty;
|
fields[entry.Key] = entry.Value ?? string.Empty;
|
||||||
|
foreach (var entry in this.multiselectDropdownFields)
|
||||||
|
fields[entry.Key] = CreateLuaArray(entry.Value);
|
||||||
foreach (var entry in this.switchFields)
|
foreach (var entry in this.switchFields)
|
||||||
fields[entry.Key] = entry.Value;
|
fields[entry.Key] = entry.Value;
|
||||||
foreach (var entry in this.webContentFields)
|
foreach (var entry in this.webContentFields)
|
||||||
@ -287,8 +290,18 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
this.inputFields.Add(textArea.Name, textArea.PrefillText);
|
this.inputFields.Add(textArea.Name, textArea.PrefillText);
|
||||||
break;
|
break;
|
||||||
case AssistantComponentType.DROPDOWN:
|
case AssistantComponentType.DROPDOWN:
|
||||||
if (component is AssistantDropdown dropdown && !this.dropdownFields.ContainsKey(dropdown.Name))
|
if (component is AssistantDropdown dropdown)
|
||||||
this.dropdownFields.Add(dropdown.Name, dropdown.Default.Display);
|
{
|
||||||
|
if (dropdown.IsMultiselect)
|
||||||
|
{
|
||||||
|
if (!this.multiselectDropdownFields.ContainsKey(dropdown.Name))
|
||||||
|
this.multiselectDropdownFields.Add(dropdown.Name, CreateInitialMultiselectValues(dropdown));
|
||||||
|
}
|
||||||
|
else if (!this.dropdownFields.ContainsKey(dropdown.Name))
|
||||||
|
{
|
||||||
|
this.dropdownFields.Add(dropdown.Name, dropdown.Default.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case AssistantComponentType.SWITCH:
|
case AssistantComponentType.SWITCH:
|
||||||
if (component is AssistantSwitch switchComponent && !this.switchFields.ContainsKey(switchComponent.Name))
|
if (component is AssistantSwitch switchComponent && !this.switchFields.ContainsKey(switchComponent.Name))
|
||||||
@ -399,6 +412,17 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.multiselectDropdownFields.ContainsKey(fieldName))
|
||||||
|
{
|
||||||
|
if (value.TryRead<LuaTable>(out var multiselectDropdownValue))
|
||||||
|
this.multiselectDropdownFields[fieldName] = ReadStringValues(multiselectDropdownValue);
|
||||||
|
else if (value.TryRead<string>(out var singleDropdownValue))
|
||||||
|
this.multiselectDropdownFields[fieldName] = string.IsNullOrWhiteSpace(singleDropdownValue) ? [] : [singleDropdownValue];
|
||||||
|
else
|
||||||
|
this.LogFieldUpdateTypeMismatch(fieldName, "string[]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.switchFields.ContainsKey(fieldName))
|
if (this.switchFields.ContainsKey(fieldName))
|
||||||
{
|
{
|
||||||
if (value.TryRead<bool>(out var boolValue))
|
if (value.TryRead<bool>(out var boolValue))
|
||||||
@ -443,6 +467,12 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
this.Logger.LogWarning("Assistant BUTTON action tried to write an invalid value to '{FieldName}'. Expected {ExpectedType}.", fieldName, expectedType);
|
this.Logger.LogWarning("Assistant BUTTON action tried to write an invalid value to '{FieldName}'. Expected {ExpectedType}.", fieldName, expectedType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private EventCallback<HashSet<string>> CreateMultiselectDropdownChangedCallback(string fieldName) =>
|
||||||
|
EventCallback.Factory.Create<HashSet<string>>(this, values =>
|
||||||
|
{
|
||||||
|
this.multiselectDropdownFields[fieldName] = values;
|
||||||
|
});
|
||||||
|
|
||||||
private string? ValidateProfileSelection(AssistantProfileSelection profileSelection, Profile? profile)
|
private string? ValidateProfileSelection(AssistantProfileSelection profileSelection, Profile? profile)
|
||||||
{
|
{
|
||||||
if (profile == default || profile == Profile.NO_PROFILE)
|
if (profile == default || profile == Profile.NO_PROFILE)
|
||||||
@ -517,7 +547,9 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
if (component is AssistantDropdown dropdown)
|
if (component is AssistantDropdown dropdown)
|
||||||
{
|
{
|
||||||
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{dropdown.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{dropdown.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||||
if (this.dropdownFields.TryGetValue(dropdown.Name, out userInput))
|
if (dropdown.IsMultiselect && this.multiselectDropdownFields.TryGetValue(dropdown.Name, out var selections))
|
||||||
|
prompt += $"user prompt:{Environment.NewLine}{string.Join(Environment.NewLine, selections.OrderBy(static value => value, StringComparer.Ordinal))}";
|
||||||
|
else if (this.dropdownFields.TryGetValue(dropdown.Name, out userInput))
|
||||||
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -567,4 +599,36 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
|
|
||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static HashSet<string> CreateInitialMultiselectValues(AssistantDropdown dropdown)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(dropdown.Default.Value))
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return [dropdown.Default.Value];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LuaTable CreateLuaArray(IEnumerable<string> values)
|
||||||
|
{
|
||||||
|
var luaArray = new LuaTable();
|
||||||
|
var index = 1;
|
||||||
|
|
||||||
|
foreach (var value in values.OrderBy(static value => value, StringComparer.Ordinal))
|
||||||
|
luaArray[index++] = value;
|
||||||
|
|
||||||
|
return luaArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashSet<string> ReadStringValues(LuaTable values)
|
||||||
|
{
|
||||||
|
var parsedValues = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var entry in values)
|
||||||
|
{
|
||||||
|
if (entry.Value.TryRead<string>(out var value) && !string.IsNullOrWhiteSpace(value))
|
||||||
|
parsedValues.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,52 @@
|
|||||||
@using AIStudio.Tools.PluginSystem.Assistants.DataModel
|
@using AIStudio.Tools.PluginSystem.Assistants.DataModel
|
||||||
<MudStack Row="true" Class='@this.MergeClasses(this.Class, "mb-3")' Style="@this.Style">
|
<MudStack Row="true" Class='@this.MergeClasses(this.Class, "mb-3")' Style="@this.Style">
|
||||||
<MudSelect
|
@if (this.IsMultiselect)
|
||||||
T="string"
|
{
|
||||||
Value="@this.Value"
|
<MudSelect
|
||||||
ValueChanged="@(val => this.OnValueChanged(val))"
|
T="string"
|
||||||
Label="@this.Label"
|
SelectedValues="@this.SelectedValues"
|
||||||
HelperText="@this.HelperText"
|
SelectedValuesChanged="@this.OnSelectedValuesChanged"
|
||||||
Placeholder="@this.Default.Value"
|
Label="@this.Label"
|
||||||
OpenIcon="@this.OpenIcon"
|
HelperText="@this.HelperText"
|
||||||
CloseIcon="@this.CloseIcon"
|
Placeholder="@this.Default.Display"
|
||||||
Adornment="@this.IconPosition"
|
OpenIcon="@this.OpenIcon"
|
||||||
AdornmentColor="@this.IconColor"
|
CloseIcon="@this.CloseIcon"
|
||||||
Variant="@this.Variant"
|
Adornment="@this.IconPosition"
|
||||||
Margin="Margin.Normal"
|
AdornmentColor="@this.IconColor"
|
||||||
MultiSelection="@this.IsMultiselect"
|
Variant="@this.Variant"
|
||||||
SelectAll="@this.HasSelectAll"
|
Margin="Margin.Normal"
|
||||||
SelectAllText="@this.SelectAllText"
|
MultiSelection="@true"
|
||||||
>
|
SelectAll="@this.HasSelectAll"
|
||||||
@foreach (var item in Items)
|
SelectAllText="@this.SelectAllText">
|
||||||
{
|
@foreach (var item in Items)
|
||||||
<MudSelectItem Value="@item.Value">
|
{
|
||||||
@item.Display
|
<MudSelectItem Value="@item.Value">
|
||||||
</MudSelectItem>
|
@item.Display
|
||||||
}
|
</MudSelectItem>
|
||||||
</MudSelect>
|
}
|
||||||
|
</MudSelect>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudSelect
|
||||||
|
T="string"
|
||||||
|
Value="@this.Value"
|
||||||
|
ValueChanged="@(val => this.OnValueChanged(val))"
|
||||||
|
Label="@this.Label"
|
||||||
|
HelperText="@this.HelperText"
|
||||||
|
Placeholder="@this.Default.Display"
|
||||||
|
OpenIcon="@this.OpenIcon"
|
||||||
|
CloseIcon="@this.CloseIcon"
|
||||||
|
Adornment="@this.IconPosition"
|
||||||
|
AdornmentColor="@this.IconColor"
|
||||||
|
Variant="@this.Variant"
|
||||||
|
Margin="Margin.Normal">
|
||||||
|
@foreach (var item in Items)
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@item.Value">
|
||||||
|
@item.Display
|
||||||
|
</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
|
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
@ -17,6 +18,10 @@ namespace AIStudio.Components
|
|||||||
|
|
||||||
[Parameter] public EventCallback<string> ValueChanged { get; set; }
|
[Parameter] public EventCallback<string> ValueChanged { get; set; }
|
||||||
|
|
||||||
|
[Parameter] public HashSet<string> SelectedValues { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter] public EventCallback<HashSet<string>> SelectedValuesChanged { get; set; }
|
||||||
|
|
||||||
[Parameter] public string Label { get; set; } = string.Empty;
|
[Parameter] public string Label { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Parameter] public string HelperText { get; set; } = string.Empty;
|
[Parameter] public string HelperText { get; set; } = string.Empty;
|
||||||
@ -52,6 +57,20 @@ namespace AIStudio.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OnSelectedValuesChanged(IEnumerable<string?>? newValues)
|
||||||
|
{
|
||||||
|
var updatedValues = newValues?
|
||||||
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||||
|
.Select(value => value!)
|
||||||
|
.ToHashSet(StringComparer.Ordinal) ?? [];
|
||||||
|
|
||||||
|
if (this.SelectedValues.SetEquals(updatedValues))
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.SelectedValues = updatedValues;
|
||||||
|
await this.SelectedValuesChanged.InvokeAsync(updatedValues);
|
||||||
|
}
|
||||||
|
|
||||||
private string MergeClasses(string custom, string fallback)
|
private string MergeClasses(string custom, string fallback)
|
||||||
{
|
{
|
||||||
var trimmedCustom = custom?.Trim() ?? string.Empty;
|
var trimmedCustom = custom?.Trim() ?? string.Empty;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user