improved dropdown component and fixed some bugs with it

This commit is contained in:
nilsk 2026-03-13 01:14:03 +01:00
parent 9aa028a1e2
commit a182cc438a
10 changed files with 138 additions and 22 deletions

View File

@ -91,9 +91,18 @@
@bind-Value="@this.dropdownFields[assistantDropdown.Name]" @bind-Value="@this.dropdownFields[assistantDropdown.Name]"
Default="@assistantDropdown.Default" Default="@assistantDropdown.Default"
Label="@assistantDropdown.Label" Label="@assistantDropdown.Label"
Icon="@Icons.Material.Filled.Translate" 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)"
IsMultiselect="@assistantDropdown.IsMultiselect"
HasSelectAll="@assistantDropdown.HasSelectAll"
SelectAllText="@assistantDropdown.SelectAllText"
Class="@assistantDropdown.Class" Class="@assistantDropdown.Class"
Style="@assistantDropdown.Style" /> Style="@this.GetOptionalStyle(assistantDropdown.Style)"
/>
} }
break; break;
case AssistantComponentType.BUTTON: case AssistantComponentType.BUTTON:

View File

@ -288,7 +288,7 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
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.ContainsKey(dropdown.Name))
this.dropdownFields.Add(dropdown.Name, dropdown.Default.Value); this.dropdownFields.Add(dropdown.Name, dropdown.Default.Display);
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))

View File

@ -1,15 +1,21 @@
@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 <MudSelect
T="string" T="string"
Value="@this.Value" Value="@this.Value"
ValueChanged="@(val => this.OnValueChanged(val))" ValueChanged="@(val => this.OnValueChanged(val))"
Label="@this.Label" Label="@this.Label"
HelperText="@this.HelperText"
Placeholder="@this.Default.Value" Placeholder="@this.Default.Value"
AdornmentIcon="@this.Icon" OpenIcon="@this.OpenIcon"
Adornment="Adornment.Start" CloseIcon="@this.CloseIcon"
Variant="Variant.Outlined" Adornment="@this.IconPosition"
Margin="Margin.Dense" AdornmentColor="@this.IconColor"
MultiSelection="false" Variant="@this.Variant"
Margin="Margin.Normal"
MultiSelection="@this.IsMultiselect"
SelectAll="@this.HasSelectAll"
SelectAllText="@this.SelectAllText"
> >
@foreach (var item in Items) @foreach (var item in Items)
{ {

View File

@ -19,9 +19,25 @@ namespace AIStudio.Components
[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 Func<string, string?> ValidateSelection { get; set; } = _ => null; [Parameter] public Func<string, string?> ValidateSelection { get; set; } = _ => null;
[Parameter] public string Icon { get; set; } = Icons.Material.Filled.ArrowDropDown; [Parameter] public string OpenIcon { get; set; } = Icons.Material.Filled.ArrowDropDown;
[Parameter] public string CloseIcon { get; set; } = Icons.Material.Filled.ArrowDropUp;
[Parameter] public Color IconColor { get; set; } = Color.Default;
[Parameter] public Adornment IconPosition { get; set; } = Adornment.End;
[Parameter] public Variant Variant { get; set; } = Variant.Outlined;
[Parameter] public bool IsMultiselect { get; set; }
[Parameter] public bool HasSelectAll { get; set; }
[Parameter] public string SelectAllText { get; set; } = string.Empty;
[Parameter] public string Class { get; set; } = string.Empty; [Parameter] public string Class { get; set; } = string.Empty;
@ -36,17 +52,14 @@ namespace AIStudio.Components
} }
} }
internal 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;
var trimmedFallback = fallback?.Trim() ?? string.Empty; var trimmedFallback = fallback?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(trimmedCustom)) if (string.IsNullOrEmpty(trimmedCustom))
return trimmedFallback; return trimmedFallback;
if (string.IsNullOrEmpty(trimmedFallback)) return string.IsNullOrEmpty(trimmedFallback) ? trimmedCustom : $"{trimmedCustom} {trimmedFallback}";
return trimmedCustom;
return $"{trimmedCustom} {trimmedFallback}";
} }
} }
} }

View File

@ -7,6 +7,7 @@ This folder keeps the Lua manifest (`plugin.lua`) that defines a custom assistan
- [How to Use This Documentation](#how-to-use-this-documentation) - [How to Use This Documentation](#how-to-use-this-documentation)
- [Directory Structure](#directory-structure) - [Directory Structure](#directory-structure)
- [Structure](#structure) - [Structure](#structure)
- [Minimal Requirements Assistant Table](#example-minimal-requirements-assistant-table)
- [Supported types (matching the Blazor UI components):](#supported-types-matching-the-blazor-ui-components) - [Supported types (matching the Blazor UI components):](#supported-types-matching-the-blazor-ui-components)
- [Component References](#component-references) - [Component References](#component-references)
- [`TEXT_AREA` reference](#text_area-reference) - [`TEXT_AREA` reference](#text_area-reference)
@ -64,6 +65,24 @@ Each assistant plugin lives in its own directory under the assistants plugin roo
- `UI.Type` is always `"FORM"` and `UI.Children` is a list of component tables. - `UI.Type` is always `"FORM"` and `UI.Children` is a list of component tables.
- Each component table declares `Type`, an optional `Children` array, and a `Props` table that feeds the components parameters. - Each component table declares `Type`, an optional `Children` array, and a `Props` table that feeds the components parameters.
### Example: Minimal Requirements Assistant Table
```lua
ASSISTANT = {
["Title"] = "",
["Description"] = "",
["SystemPrompt"] = "",
["SubmitText"] = "",
["AllowProfiles"] = true,
["UI"] = {
["Type"] = "FORM",
["Children"] = {
-- Components
}
},
}
```
#### Supported types (matching the Blazor UI components): #### Supported types (matching the Blazor UI components):
- `TEXT_AREA`: user input field based on `MudTextField`; requires `Name`, `Label`, and may include `HelperText`, `HelperTextOnFocus`, `Adornment`, `AdornmentIcon`, `AdornmentText`, `AdornmentColor`, `Counter`, `MaxLength`, `IsImmediate`, `UserPrompt`, `PrefillText`, `IsSingleLine`, `ReadOnly`, `Class`, `Style`. - `TEXT_AREA`: user input field based on `MudTextField`; requires `Name`, `Label`, and may include `HelperText`, `HelperTextOnFocus`, `Adornment`, `AdornmentIcon`, `AdornmentText`, `AdornmentColor`, `Counter`, `MaxLength`, `IsImmediate`, `UserPrompt`, `PrefillText`, `IsSingleLine`, `ReadOnly`, `Class`, `Style`.
@ -692,5 +711,6 @@ LogInfo(dt.day .. "." .. dt.month .. "." .. dt.year)
## Useful Resources ## Useful Resources
- [plugin.lua - Lua Manifest](https://github.com/MindWorkAI/AI-Studio/tree/main/app/MindWork%20AI%20Studio/Plugins/assistants/plugin.lua) - [plugin.lua - Lua Manifest](https://github.com/MindWorkAI/AI-Studio/tree/main/app/MindWork%20AI%20Studio/Plugins/assistants/plugin.lua)
- [AI Studio Repository](https://github.com/MindWorkAI/AI-Studio/)
- [Lua 5.2 Reference Manual](https://www.lua.org/manual/5.2/manual.html) - [Lua 5.2 Reference Manual](https://www.lua.org/manual/5.2/manual.html)
- [MudBlazor Documentation](https://www.mudblazor.com/docs/overview) - [MudBlazor Documentation](https://www.mudblazor.com/docs/overview)

View File

@ -70,7 +70,7 @@ ASSISTANT = {
["Props"] = { ["Props"] = {
["Name"] = "<unique identifier of this component>", -- required ["Name"] = "<unique identifier of this component>", -- required
["Label"] = "<heading of your component>", -- required ["Label"] = "<heading of your component>", -- required
["Adornment"] = "<Start|End|None>", -- location of the `AdornmentIcon` OR `AdornmentText`; CASE SENSITIV ["Adornment"] = "<Start|End|None>", -- location of the `AdornmentIcon` OR `AdornmentText`; CASE SENSITIVE
["AdornmentIcon"] = "Icons.Material.Filled.AppSettingsAlt", -- The Mudblazor icon displayed for the adornment ["AdornmentIcon"] = "Icons.Material.Filled.AppSettingsAlt", -- The Mudblazor icon displayed for the adornment
["AdornmentText"] = "", -- The text displayed for the adornment ["AdornmentText"] = "", -- The text displayed for the adornment
["AdornmentColor"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>", -- the color of AdornmentText or AdornmentIcon; CASE SENSITIVE ["AdornmentColor"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>", -- the color of AdornmentText or AdornmentIcon; CASE SENSITIVE
@ -90,10 +90,19 @@ ASSISTANT = {
{ {
["Type"] = "DROPDOWN", -- required ["Type"] = "DROPDOWN", -- required
["Props"] = { ["Props"] = {
["Name"] = "<unique identifier of this component>", -- required ["Name"] = "<unique identifier of component>", -- required
["Label"] = "<heading of your component>", -- required ["Label"] = "<heading of component>", -- required
["UserPrompt"] = "<direct input of instructions, questions, or tasks by a user>", ["UserPrompt"] = "<direct input of instructions, questions, or tasks by a user>",
["ValueType"] = "<data type of item values>", -- required ["IsMultiselect"] = false,
["HasSelectAll"] = false,
["SelectAllText"] = "<label for 'SelectAll'-Button",
["HelperText"] = "<helping text rendered under the component>",
["OpenIcon"] = "Icons.Material.Filled.ArrowDropDown",
["OpenClose"] = "Icons.Material.Filled.ArrowDropUp",
["IconColor"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>",
["IconPositon"] = "<Start|End>",
["Variant"] = "<Text|Filled|Outlined>",
["ValueType"] = "<string|int|bool>", -- required
["Default"] = { ["Value"] = "<internal data>", ["Display"] = "<user readable representation>" }, -- required ["Default"] = { ["Value"] = "<internal data>", ["Display"] = "<user readable representation>" }, -- required
["Items"] = { ["Items"] = {
{ ["Value"] = "<internal data>", ["Display"] = "<user readable representation>" }, { ["Value"] = "<internal data>", ["Display"] = "<user readable representation>" },

View File

@ -61,6 +61,8 @@ internal static class AssistantComponentPropHelper
} }
public static MudBlazor.Color GetColor(string value, Color fallback) => Enum.TryParse<MudBlazor.Color>(value, out var color) ? color : fallback; public static MudBlazor.Color GetColor(string value, Color fallback) => Enum.TryParse<MudBlazor.Color>(value, out var color) ? color : fallback;
public static MudBlazor.Variant GetVariant(string value, Variant fallback) => Enum.TryParse<MudBlazor.Variant>(value, out var variant) ? variant : fallback;
public static MudBlazor.Adornment GetAdornment(string value, Adornment fallback) => Enum.TryParse<MudBlazor.Adornment>(value, out var adornment) ? adornment : fallback;
public static string GetIconSvg(string value) => MudBlazorIconRegistry.TryGetSvg(value, out var svg) ? svg : string.Empty; public static string GetIconSvg(string value) => MudBlazorIconRegistry.TryGetSvg(value, out var svg) ? svg : string.Empty;
public static Size GetComponentSize(string value, Size fallback) => Enum.TryParse<Size>(value, out var size) ? size : fallback; public static Size GetComponentSize(string value, Size fallback) => Enum.TryParse<Size>(value, out var size) ? size : fallback;
public static Justify? GetJustify(string value) => Enum.TryParse<Justify>(value, out var justify) ? justify : null; public static Justify? GetJustify(string value) => Enum.TryParse<Justify>(value, out var justify) ? justify : null;

View File

@ -52,6 +52,60 @@ internal sealed class AssistantDropdown : AssistantComponentBase
set => this.Props[nameof(this.ValueType)] = value; set => this.Props[nameof(this.ValueType)] = value;
} }
public bool IsMultiselect
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.IsMultiselect), false);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.IsMultiselect), value);
}
public bool HasSelectAll
{
get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.HasSelectAll), false);
set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.HasSelectAll), value);
}
public string SelectAllText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.SelectAllText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.SelectAllText), value);
}
public string HelperText
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.HelperText));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.HelperText), value);
}
public string OpenIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.OpenIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.OpenIcon), value);
}
public string CloseIcon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.CloseIcon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.CloseIcon), value);
}
public string IconColor
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.IconColor));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.IconColor), value);
}
public string IconPositon
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.IconPositon));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.IconPositon), value);
}
public string Variant
{
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Variant));
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Variant), value);
}
public IEnumerable<object> GetParsedDropdownValues() public IEnumerable<object> GetParsedDropdownValues()
{ {
foreach (var item in this.Items) foreach (var item in this.Items)

View File

@ -1,6 +1,6 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public class AssistantDropdownItem public sealed class AssistantDropdownItem
{ {
public string Value { get; set; } = string.Empty; public string Value { get; set; } = string.Empty;
public string Display { get; set; } = string.Empty; public string Display { get; set; } = string.Empty;

View File

@ -30,7 +30,10 @@ public static class ComponentPropSpecs
), ),
[AssistantComponentType.DROPDOWN] = new( [AssistantComponentType.DROPDOWN] = new(
required: ["Name", "Label", "Default", "Items"], required: ["Name", "Label", "Default", "Items"],
optional: ["UserPrompt", "Class", "Style"] optional: [
"UserPrompt", "IsMultiselect", "HasSelectAll", "SelectAllText", "HelperText",
"OpenIcon", "CloseIcon", "IconColor", "IconPositon", "Variant", "Class", "Style"
]
), ),
[AssistantComponentType.PROVIDER_SELECTION] = new( [AssistantComponentType.PROVIDER_SELECTION] = new(
required: ["Name", "Label"], required: ["Name", "Label"],