diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
index d014a3c0..7b729b23 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
+++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
@@ -87,22 +87,42 @@
case AssistantComponentType.DROPDOWN:
if (component is AssistantDropdown assistantDropdown)
{
-
+ if (assistantDropdown.IsMultiselect)
+ {
+
+ }
+ else
+ {
+
+ }
}
break;
case AssistantComponentType.BUTTON:
diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs
index cc68282d..fa0d31be 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs
+++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs
@@ -43,6 +43,7 @@ public partial class AssistantDynamic : AssistantBaseCore
private readonly Dictionary inputFields = new();
private readonly Dictionary dropdownFields = new();
+ private readonly Dictionary> multiselectDropdownFields = new();
private readonly Dictionary switchFields = new();
private readonly Dictionary webContentFields = new();
private readonly Dictionary fileContentFields = new();
@@ -218,6 +219,8 @@ public partial class AssistantDynamic : AssistantBaseCore
fields[entry.Key] = entry.Value ?? string.Empty;
foreach (var entry in this.dropdownFields)
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)
fields[entry.Key] = entry.Value;
foreach (var entry in this.webContentFields)
@@ -287,8 +290,18 @@ public partial class AssistantDynamic : AssistantBaseCore
this.inputFields.Add(textArea.Name, textArea.PrefillText);
break;
case AssistantComponentType.DROPDOWN:
- if (component is AssistantDropdown dropdown && !this.dropdownFields.ContainsKey(dropdown.Name))
- this.dropdownFields.Add(dropdown.Name, dropdown.Default.Display);
+ if (component is AssistantDropdown dropdown)
+ {
+ 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;
case AssistantComponentType.SWITCH:
if (component is AssistantSwitch switchComponent && !this.switchFields.ContainsKey(switchComponent.Name))
@@ -399,6 +412,17 @@ public partial class AssistantDynamic : AssistantBaseCore
return;
}
+ if (this.multiselectDropdownFields.ContainsKey(fieldName))
+ {
+ if (value.TryRead(out var multiselectDropdownValue))
+ this.multiselectDropdownFields[fieldName] = ReadStringValues(multiselectDropdownValue);
+ else if (value.TryRead(out var singleDropdownValue))
+ this.multiselectDropdownFields[fieldName] = string.IsNullOrWhiteSpace(singleDropdownValue) ? [] : [singleDropdownValue];
+ else
+ this.LogFieldUpdateTypeMismatch(fieldName, "string[]");
+ return;
+ }
+
if (this.switchFields.ContainsKey(fieldName))
{
if (value.TryRead(out var boolValue))
@@ -443,6 +467,12 @@ public partial class AssistantDynamic : AssistantBaseCore
this.Logger.LogWarning("Assistant BUTTON action tried to write an invalid value to '{FieldName}'. Expected {ExpectedType}.", fieldName, expectedType);
}
+ private EventCallback> CreateMultiselectDropdownChangedCallback(string fieldName) =>
+ EventCallback.Factory.Create>(this, values =>
+ {
+ this.multiselectDropdownFields[fieldName] = values;
+ });
+
private string? ValidateProfileSelection(AssistantProfileSelection profileSelection, Profile? profile)
{
if (profile == default || profile == Profile.NO_PROFILE)
@@ -517,7 +547,9 @@ public partial class AssistantDynamic : AssistantBaseCore
if (component is AssistantDropdown dropdown)
{
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}";
}
break;
@@ -567,4 +599,36 @@ public partial class AssistantDynamic : AssistantBaseCore
return prompt;
}
+
+ private static HashSet CreateInitialMultiselectValues(AssistantDropdown dropdown)
+ {
+ if (string.IsNullOrWhiteSpace(dropdown.Default.Value))
+ return [];
+
+ return [dropdown.Default.Value];
+ }
+
+ private static LuaTable CreateLuaArray(IEnumerable 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 ReadStringValues(LuaTable values)
+ {
+ var parsedValues = new HashSet(StringComparer.Ordinal);
+
+ foreach (var entry in values)
+ {
+ if (entry.Value.TryRead(out var value) && !string.IsNullOrWhiteSpace(value))
+ parsedValues.Add(value);
+ }
+
+ return parsedValues;
+ }
}
diff --git a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor
index 9ad8880a..8723138f 100644
--- a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor
+++ b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor
@@ -1,27 +1,52 @@
@using AIStudio.Tools.PluginSystem.Assistants.DataModel
-
- @foreach (var item in Items)
- {
-
- @item.Display
-
- }
-
+ @if (this.IsMultiselect)
+ {
+
+ @foreach (var item in Items)
+ {
+
+ @item.Display
+
+ }
+
+ }
+ else
+ {
+
+ @foreach (var item in Items)
+ {
+
+ @item.Display
+
+ }
+
+ }
diff --git a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs
index 22896792..df59aa3c 100644
--- a/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs
+++ b/app/MindWork AI Studio/Components/DynamicAssistantDropdown.razor.cs
@@ -1,5 +1,6 @@
-using System;
+using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
using Microsoft.AspNetCore.Components;
@@ -17,6 +18,10 @@ namespace AIStudio.Components
[Parameter] public EventCallback ValueChanged { get; set; }
+ [Parameter] public HashSet SelectedValues { get; set; } = [];
+
+ [Parameter] public EventCallback> SelectedValuesChanged { get; set; }
+
[Parameter] public string Label { get; set; } = string.Empty;
[Parameter] public string HelperText { get; set; } = string.Empty;
@@ -52,6 +57,20 @@ namespace AIStudio.Components
}
}
+ private async Task OnSelectedValuesChanged(IEnumerable? 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)
{
var trimmedCustom = custom?.Trim() ?? string.Empty;
@@ -62,4 +81,4 @@ namespace AIStudio.Components
return string.IsNullOrEmpty(trimmedFallback) ? trimmedCustom : $"{trimmedCustom} {trimmedFallback}";
}
}
-}
\ No newline at end of file
+}