From dbdcdef83c257ea121cfccf082570d0d86809973 Mon Sep 17 00:00:00 2001 From: krut_ni Date: Tue, 10 Mar 2026 16:12:00 +0100 Subject: [PATCH] Adding a ButtonGroup component to group button children together --- .../Assistants/Dynamic/AssistantDynamic.razor | 37 +++++++++++- .../Plugins/assistants/README.md | 59 +++++++++++++++++++ .../Plugins/assistants/plugin.lua | 16 +++++ .../Assistants/AssistantComponentFactory.cs | 2 + .../DataModel/AssistantButtonGroup.cs | 58 ++++++++++++++++++ .../DataModel/AssistantComponentType.cs | 1 + .../DataModel/ComponentPropSpecs.cs | 4 ++ .../Assistants/PluginAssistants.cs | 10 ++++ 8 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButtonGroup.cs diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor index 726ce248..18d453d6 100644 --- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor +++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor @@ -103,7 +103,42 @@ Class='@MergeClass(button.Class, "mb-3")' Style="@this.GetOptionalStyle(button.Style)"> @button.Text - + + } + break; + case AssistantComponentType.BUTTON_GROUP: + if (component is AssistantButtonGroup assistantButtonGroup) + { + var buttonGroup = assistantButtonGroup; + + @foreach (var child in buttonGroup.Children) + { + if (child is AssistantButton childButton) + { + + @childButton.Text + + } + } + } break; case AssistantComponentType.PROVIDER_SELECTION: diff --git a/app/MindWork AI Studio/Plugins/assistants/README.md b/app/MindWork AI Studio/Plugins/assistants/README.md index f0ec6565..8800b004 100644 --- a/app/MindWork AI Studio/Plugins/assistants/README.md +++ b/app/MindWork AI Studio/Plugins/assistants/README.md @@ -12,6 +12,7 @@ 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`. - `DROPDOWN`: selects between variants; `Props` must include `Name`, `Label`, `Default`, `Items`, and optionally `ValueType` plus `UserPrompt`. - `BUTTON`: invokes a Lua callback; `Props` must include `Name`, `Text`, `Action`, and may include `Variant`, `Color`, `IsFullWidth`, `Size`, `StartIcon`, `EndIcon`, `IconColor`, `IconSize`, `Class`, `Style`. +- `BUTTON_GROUP`: groups multiple `BUTTON` children in a `MudButtonGroup`; `Children` must contain only `BUTTON` components and `Props` may include `Variant`, `Color`, `Size`, `OverrideStyles`, `Vertical`, `DropShadow`, `Class`, `Style`. - `SWITCH`: boolean option; requires `Name`, `Label`, `Value`, and may include `Disabled`, `UserPrompt`, `LabelOn`, `LabelOff`, `LabelPlacement`, `Icon`, `IconColor`, `CheckedColor`, `UncheckedColor`, `Class`, `Style`. - `COLOR_PICKER`: color input based on `MudColorPicker`; requires `Name`, `Label`, and may include `Placeholder`, `ShowAlpha`, `ShowToolbar`, `ShowModeSwitch`, `PickerVariant`, `UserPrompt`, `Class`, `Style`. - `PROVIDER_SELECTION` / `PROFILE_SELECTION`: hooks into the shared provider/profile selectors. @@ -221,6 +222,64 @@ Example: } ``` +### `BUTTON_GROUP` reference +- Use `Type = "BUTTON_GROUP"` to render multiple `BUTTON` children as a single MudBlazor button group. +- Required structure: + - `Children`: array of `BUTTON` component tables. Other child component types are ignored. +- Optional props: + - `Variant`: one of the MudBlazor `Variant` enum names such as `Filled`, `Outlined`, `Text`; omitted values fall back to `Filled`. + - `Color`: one of the MudBlazor `Color` enum names such as `Default`, `Primary`, `Secondary`, `Info`; omitted values fall back to `Default`. + - `Size`: one of the MudBlazor `Size` enum names such as `Small`, `Medium`, `Large`; omitted values fall back to `Medium`. + - `OverrideStyles`: defaults to `false`; enables MudBlazor button-group style overrides. + - `Vertical`: defaults to `false`; when `true`, buttons are rendered vertically instead of horizontally. + - `DropShadow`: defaults to `true`; controls the group shadow. + - `Class`, `Style`: forwarded to the rendered `MudButtonGroup` for layout/styling. +- Child buttons use the existing `BUTTON` props and behavior, including Lua `Action(input)`. + +Example: +```lua +{ + ["Type"] = "BUTTON_GROUP", + ["Props"] = { + ["Variant"] = "Filled", + ["Color"] = "Primary", + ["Size"] = "Medium", + ["OverrideStyles"] = false, + ["Vertical"] = false, + ["DropShadow"] = true + }, + ["Children"] = { + { + ["Type"] = "BUTTON", + ["Props"] = { + ["Name"] = "buildEmailOutput", + ["Text"] = "Build output", + ["Action"] = function(input) + return { + fields = { + outputBuffer = input.fields.emailContent or "" + } + } + end, + ["StartIcon"] = "Icons.Material.Filled.Build" + } + }, + { + ["Type"] = "BUTTON", + ["Props"] = { + ["Name"] = "logColor", + ["Text"] = "Log color", + ["Action"] = function(input) + LogError("ColorPicker value: " .. tostring(input.fields.colorPicker or "")) + return nil + end, + ["EndIcon"] = "Icons.Material.Filled.BugReport" + } + } + } +} +``` + ### `SWITCH` reference - Use `Type = "SWITCH"` to render a boolean toggle. - Required props: diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index 6b5cbab5..084e90d2 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -152,6 +152,22 @@ ASSISTANT = { ["Style"] = "", } }, + { + ["Type"] = "BUTTON_GROUP", + ["Props"] = { + ["Variant"] = "", -- display variation of the group. Defaults to Filled + ["Color"] = "", -- color of the group. Defaults to Default + ["Size"] = "", -- size of the group. Defaults to Medium + ["OverrideStyles"] = false, -- allows MudBlazor group style overrides. Defaults to false + ["Vertical"] = false, -- renders buttons vertically instead of horizontally. Defaults to false + ["DropShadow"] = true, -- applies a group shadow. Defaults to true + ["Class"] = "", + ["Style"] = "", + }, + ["Children"] = { + -- BUTTON_ELEMENTS + } + }, { ["Type"] = "PROVIDER_SELECTION", -- required ["Props"] = { diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs index e5078a3b..c611b4f6 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/AssistantComponentFactory.cs @@ -19,6 +19,8 @@ public class AssistantComponentFactory return new AssistantTextArea { Props = props, Children = children }; case AssistantComponentType.BUTTON: return new AssistantButton { Props = props, Children = children}; + case AssistantComponentType.BUTTON_GROUP: + return new AssistantButtonGroup { Props = props, Children = children }; case AssistantComponentType.DROPDOWN: return new AssistantDropdown { Props = props, Children = children }; case AssistantComponentType.PROVIDER_SELECTION: diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButtonGroup.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButtonGroup.cs new file mode 100644 index 00000000..668cd841 --- /dev/null +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantButtonGroup.cs @@ -0,0 +1,58 @@ +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; + +public sealed class AssistantButtonGroup : AssistantComponentBase +{ + public override AssistantComponentType Type => AssistantComponentType.BUTTON_GROUP; + public override Dictionary Props { get; set; } = new(); + public override List Children { get; set; } = new(); + + public string Variant + { + get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Variant)); + set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Variant), value); + } + + public string Color + { + get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Color)); + set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Color), value); + } + + public string Size + { + get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Size)); + set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Size), value); + } + + public bool OverrideStyles + { + get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.OverrideStyles), false); + set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.OverrideStyles), value); + } + + public bool Vertical + { + get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.Vertical), false); + set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.Vertical), value); + } + + public bool DropShadow + { + get => AssistantComponentPropHelper.ReadBool(this.Props, nameof(this.DropShadow), true); + set => AssistantComponentPropHelper.WriteBool(this.Props, nameof(this.DropShadow), value); + } + + public string Class + { + get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class)); + set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Class), value); + } + + public string Style + { + get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Style)); + set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.Style), value); + } + + public Variant GetVariant() => Enum.TryParse(this.Variant, out var variant) ? variant : MudBlazor.Variant.Filled; +} diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs index d967860a..9a494d22 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs @@ -5,6 +5,7 @@ public enum AssistantComponentType FORM, TEXT_AREA, BUTTON, + BUTTON_GROUP, DROPDOWN, PROVIDER_SELECTION, PROFILE_SELECTION, diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs index a5e521e6..9797b0ed 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/ComponentPropSpecs.cs @@ -24,6 +24,10 @@ public static class ComponentPropSpecs "StartIcon", "EndIcon", "IconColor", "IconSize", "Class", "Style" ] ), + [AssistantComponentType.BUTTON_GROUP] = new( + required: [], + optional: ["Variant", "Color", "Size", "OverrideStyles", "Vertical", "DropShadow", "Class", "Style"] + ), [AssistantComponentType.DROPDOWN] = new( required: ["Name", "Label", "Default", "Items"], optional: ["UserPrompt", "Class", "Style"] diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index 42833d78..204c274c 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -288,6 +288,16 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType textArea.IsImmediate = true; } + if (component is AssistantButtonGroup buttonGroup) + { + var invalidChildren = buttonGroup.Children.Where(child => child.Type != AssistantComponentType.BUTTON).ToList(); + if (invalidChildren.Count > 0) + { + LOGGER.LogWarning("Assistant plugin '{PluginName}' BUTTON_GROUP contains non-BUTTON children. Only BUTTON children are supported and invalid children are ignored.", this.Name); + buttonGroup.Children = buttonGroup.Children.Where(child => child.Type == AssistantComponentType.BUTTON).ToList(); + } + } + return true; }