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]"
Default="@assistantDropdown.Default"
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"
Style="@assistantDropdown.Style" />
Style="@this.GetOptionalStyle(assistantDropdown.Style)"
/>
}
break;
case AssistantComponentType.BUTTON:

View File

@ -288,7 +288,7 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
break;
case AssistantComponentType.DROPDOWN:
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;
case AssistantComponentType.SWITCH:
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">
<MudSelect
T="string"
Value="@this.Value"
ValueChanged="@(val => this.OnValueChanged(val))"
Label="@this.Label"
HelperText="@this.HelperText"
Placeholder="@this.Default.Value"
AdornmentIcon="@this.Icon"
Adornment="Adornment.Start"
Variant="Variant.Outlined"
Margin="Margin.Dense"
MultiSelection="false"
OpenIcon="@this.OpenIcon"
CloseIcon="@this.CloseIcon"
Adornment="@this.IconPosition"
AdornmentColor="@this.IconColor"
Variant="@this.Variant"
Margin="Margin.Normal"
MultiSelection="@this.IsMultiselect"
SelectAll="@this.HasSelectAll"
SelectAllText="@this.SelectAllText"
>
@foreach (var item in Items)
{

View File

@ -18,13 +18,29 @@ namespace AIStudio.Components
[Parameter] public EventCallback<string> ValueChanged { get; set; }
[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 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 Style { get; set; } = string.Empty;
private async Task OnValueChanged(string newValue)
@ -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 trimmedFallback = fallback?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(trimmedCustom))
return trimmedFallback;
if (string.IsNullOrEmpty(trimmedFallback))
return trimmedCustom;
return $"{trimmedCustom} {trimmedFallback}";
return string.IsNullOrEmpty(trimmedFallback) ? trimmedCustom : $"{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)
- [Directory Structure](#directory-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)
- [Component References](#component-references)
- [`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.
- 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):
- `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
- [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)
- [MudBlazor Documentation](https://www.mudblazor.com/docs/overview)

View File

@ -70,7 +70,7 @@ ASSISTANT = {
["Props"] = {
["Name"] = "<unique identifier of this 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
["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
@ -90,10 +90,19 @@ ASSISTANT = {
{
["Type"] = "DROPDOWN", -- required
["Props"] = {
["Name"] = "<unique identifier of this component>", -- required
["Label"] = "<heading of your component>", -- required
["Name"] = "<unique identifier of component>", -- required
["Label"] = "<heading of component>", -- required
["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
["Items"] = {
{ ["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.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 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;

View File

@ -51,6 +51,60 @@ internal sealed class AssistantDropdown : AssistantComponentBase
: "string";
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()
{

View File

@ -1,9 +1,9 @@
namespace AIStudio.Tools.PluginSystem.Assistants.DataModel;
public class AssistantDropdownItem
public sealed class AssistantDropdownItem
{
public string Value { get; set; } = string.Empty;
public string Display { get; set; } = string.Empty;
public static AssistantDropdownItem Default() => new() { Value = string.Empty, Display = string.Empty };
public static AssistantDropdownItem Default() => new() { Value = string.Empty, Display = string.Empty};
}

View File

@ -30,7 +30,10 @@ public static class ComponentPropSpecs
),
[AssistantComponentType.DROPDOWN] = new(
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(
required: ["Name", "Label"],