fixed diverse little issues that occured while testing

This commit is contained in:
nilsk 2026-03-17 17:20:16 +01:00
parent 9440f132c6
commit 2117df5b9e
10 changed files with 96 additions and 40 deletions

View File

@ -20,7 +20,22 @@ else
} }
@code { @code {
private RenderFragment RenderChildren(IEnumerable<IAssistantComponent> children) => @<text> private RenderFragment RenderSwitch(AssistantSwitch assistantSwitch) => @<MudSwitch T="bool"
Value="@switchFields[assistantSwitch.Name]"
ValueChanged="@((bool value) => ExecuteSwitchChangedAsync(assistantSwitch, value))"
LabelPlacement="@assistantSwitch.GetLabelPlacement()"
Color="@assistantSwitch.GetColor(assistantSwitch.CheckedColor)"
UncheckedColor="@assistantSwitch.GetColor(assistantSwitch.UncheckedColor)"
ThumbIcon="@assistantSwitch.GetIconSvg()"
ThumbIconColor="@assistantSwitch.GetColor(assistantSwitch.IconColor)"
Disabled="@(assistantSwitch.Disabled || IsSwitchActionRunning(assistantSwitch.Name))"
Class="@assistantSwitch.Class"
Style="@GetOptionalStyle(assistantSwitch.Style)">
@(switchFields[assistantSwitch.Name] ? assistantSwitch.LabelOn : assistantSwitch.LabelOff)
</MudSwitch>;
}
@code {private RenderFragment RenderChildren(IEnumerable<IAssistantComponent> children) => @<text>
@foreach (var child in children) @foreach (var child in children)
{ {
@this.RenderComponent(child) @this.RenderComponent(child)
@ -145,12 +160,11 @@ else
var iconSize = AssistantComponentPropHelper.GetComponentSize(button.IconSize, Size.Medium); var iconSize = AssistantComponentPropHelper.GetComponentSize(button.IconSize, Size.Medium);
var variant = button.GetButtonVariant(); var variant = button.GetButtonVariant();
var disabled = this.IsButtonActionRunning(button.Name); var disabled = this.IsButtonActionRunning(button.Name);
var buttonClass = MergeClass(button.Class, "mb-3"); var buttonClass = MergeClass(button.Class, "");
var style = this.GetOptionalStyle(button.Style); var style = this.GetOptionalStyle(button.Style);
if (!button.IsIconButton) if (!button.IsIconButton)
{ {
<div>
<MudButton Variant="@variant" <MudButton Variant="@variant"
Color="@color" Color="@color"
OnClick="@(() => this.ExecuteButtonActionAsync(button))" OnClick="@(() => this.ExecuteButtonActionAsync(button))"
@ -165,7 +179,6 @@ else
Style="@style"> Style="@style">
@button.Text @button.Text
</MudButton> </MudButton>
</div>
} }
else else
{ {
@ -314,23 +327,17 @@ else
if (component is AssistantSwitch switchComponent) if (component is AssistantSwitch switchComponent)
{ {
var assistantSwitch = switchComponent; var assistantSwitch = switchComponent;
var currentValue = this.switchFields[assistantSwitch.Name];
var disabled = assistantSwitch.Disabled || this.IsSwitchActionRunning(assistantSwitch.Name); if (string.IsNullOrEmpty(assistantSwitch.Label))
<MudField Label="@assistantSwitch.Label" Variant="Variant.Outlined" Class="mb-3" Disabled="@disabled"> {
<MudSwitch T="bool" @this.RenderSwitch(assistantSwitch)
Value="@currentValue" }
ValueChanged="@((bool value) => this.ExecuteSwitchChangedAsync(assistantSwitch, value))" else
LabelPlacement="@assistantSwitch.GetLabelPlacement()" {
Color="@assistantSwitch.GetColor(assistantSwitch.CheckedColor)" <MudField Label="@assistantSwitch.Label" Variant="Variant.Outlined" Class="mb-3" Disabled="@assistantSwitch.Disabled">
UncheckedColor="@assistantSwitch.GetColor(assistantSwitch.UncheckedColor)" @this.RenderSwitch(assistantSwitch)
ThumbIcon="@assistantSwitch.GetIconSvg()" </MudField>
ThumbIconColor="@assistantSwitch.GetColor(assistantSwitch.IconColor)" }
Disabled="@disabled"
Class="@assistantSwitch.Class"
Style="@this.GetOptionalStyle(assistantSwitch.Style)">
@(currentValue ? assistantSwitch.LabelOn : assistantSwitch.LabelOff)
</MudSwitch>
</MudField>
} }
break; break;
case AssistantComponentType.HEADING: case AssistantComponentType.HEADING:
@ -368,13 +375,16 @@ else
<MudList T="string" Class='@MergeClass(list.Class, "mb-6")' Style="@this.GetOptionalStyle(list.Style)"> <MudList T="string" Class='@MergeClass(list.Class, "mb-6")' Style="@this.GetOptionalStyle(list.Style)">
@foreach (var item in list.Items) @foreach (var item in list.Items)
{ {
var iconColor = AssistantComponentPropHelper.GetColor(item.IconColor, Color.Default);
@if (item.Type == "LINK") @if (item.Type == "LINK")
{ {
<MudListItem T="string" Icon="@Icons.Material.Filled.Link" Target="_blank" Href="@item.Href">@item.Text</MudListItem> <MudListItem T="string" Icon="@Icons.Material.Filled.Link" IconColor="@iconColor" Target="_blank" Href="@item.Href">@item.Text</MudListItem>
} }
else else
{ {
<MudListItem T="string">@item.Text</MudListItem> var icon = !string.IsNullOrEmpty(item.Icon) ? AssistantComponentPropHelper.GetIconSvg(item.Icon) : string.Empty;
<MudListItem T="string" Icon="@icon" IconColor="@iconColor">@item.Text</MudListItem>
} }
} }
</MudList> </MudList>
@ -385,7 +395,6 @@ else
{ {
var colorPicker = assistantColorPicker; var colorPicker = assistantColorPicker;
var variant = colorPicker.GetPickerVariant(); var variant = colorPicker.GetPickerVariant();
var elevation = variant == PickerVariant.Static ? 6 : 0;
var rounded = variant == PickerVariant.Static; var rounded = variant == PickerVariant.Static;
<MudItem Class="d-flex"> <MudItem Class="d-flex">
@ -397,8 +406,8 @@ else
ShowModeSwitch="@colorPicker.ShowModeSwitch" ShowModeSwitch="@colorPicker.ShowModeSwitch"
PickerVariant="@variant" PickerVariant="@variant"
Rounded="@rounded" Rounded="@rounded"
Elevation="@elevation" Elevation="@colorPicker.Elevation"
Style="@($"color: {this.colorPickerFields[colorPicker.Name]};")" Style="@($"color: {this.colorPickerFields[colorPicker.Name]};{colorPicker.Style}")"
Class="@MergeClass(colorPicker.Class, "mb-3")" /> Class="@MergeClass(colorPicker.Class, "mb-3")" />
</MudItem> </MudItem>
} }
@ -417,6 +426,7 @@ else
Placeholder="@datePicker.Placeholder" Placeholder="@datePicker.Placeholder"
HelperText="@datePicker.HelperText" HelperText="@datePicker.HelperText"
DateFormat="@format" DateFormat="@format"
Elevation="@datePicker.Elevation"
PickerVariant="@AssistantComponentPropHelper.GetPickerVariant(datePicker.PickerVariant, PickerVariant.Static)" PickerVariant="@AssistantComponentPropHelper.GetPickerVariant(datePicker.PickerVariant, PickerVariant.Static)"
Variant="Variant.Outlined" Variant="Variant.Outlined"
Class='@MergeClass(datePicker.Class, "mb-3")' Class='@MergeClass(datePicker.Class, "mb-3")'
@ -441,6 +451,7 @@ else
HelperText="@dateRangePicker.HelperText" HelperText="@dateRangePicker.HelperText"
DateFormat="@format" DateFormat="@format"
PickerVariant="@AssistantComponentPropHelper.GetPickerVariant(dateRangePicker.PickerVariant, PickerVariant.Static)" PickerVariant="@AssistantComponentPropHelper.GetPickerVariant(dateRangePicker.PickerVariant, PickerVariant.Static)"
Elevation="@dateRangePicker.Elevation"
Variant="Variant.Outlined" Variant="Variant.Outlined"
Class='@MergeClass(dateRangePicker.Class, "mb-3")' Class='@MergeClass(dateRangePicker.Class, "mb-3")'
Style="@this.GetOptionalStyle(dateRangePicker.Style)" Style="@this.GetOptionalStyle(dateRangePicker.Style)"
@ -464,6 +475,7 @@ else
TimeFormat="@format" TimeFormat="@format"
AmPm="@timePicker.AmPm" AmPm="@timePicker.AmPm"
PickerVariant="@AssistantComponentPropHelper.GetPickerVariant(timePicker.PickerVariant, PickerVariant.Static)" PickerVariant="@AssistantComponentPropHelper.GetPickerVariant(timePicker.PickerVariant, PickerVariant.Static)"
Elevation="@timePicker.Elevation"
Variant="Variant.Outlined" Variant="Variant.Outlined"
Class='@MergeClass(timePicker.Class, "mb-3")' Class='@MergeClass(timePicker.Class, "mb-3")'
Style="@this.GetOptionalStyle(timePicker.Style)"/> Style="@this.GetOptionalStyle(timePicker.Style)"/>

View File

@ -130,7 +130,8 @@ Images referenced via the `plugin://` scheme must exist in the plugin directory
| `TIME_PICKER` | `Name`, `Label` | `Value`, `Placeholder`, `HelperText`, `TimeFormat`, `AmPm`, `PickerVariant`, `UserPrompt`, `Class`, `Style` | [MudTimePicker](https://www.mudblazor.com/components/timepicker) | | `TIME_PICKER` | `Name`, `Label` | `Value`, `Placeholder`, `HelperText`, `TimeFormat`, `AmPm`, `PickerVariant`, `UserPrompt`, `Class`, `Style` | [MudTimePicker](https://www.mudblazor.com/components/timepicker) |
| `HEADING` | `Text` | `Level` | [MudText Typo="Typo.<h2\|h3\|h4\|h5\|h6>"](https://www.mudblazor.com/components/typography) | | `HEADING` | `Text` | `Level` | [MudText Typo="Typo.<h2\|h3\|h4\|h5\|h6>"](https://www.mudblazor.com/components/typography) |
| `TEXT` | `Content` | `None` | [MudText Typo="Typo.body1"](https://www.mudblazor.com/components/typography) | | `TEXT` | `Content` | `None` | [MudText Typo="Typo.body1"](https://www.mudblazor.com/components/typography) |
| `LIST` | `Type`, `Text` | `Href` | [MudList](https://www.mudblazor.com/componentss/list) | | `LIST` | `None` | `Items (LIST_ITEM)`, `Class`, `Style` | [MudList](https://www.mudblazor.com/componentss/list) |
| `LIST_ITEM` | `Type`, `Text` | `Href`, `Icon`, `IconColor` | [MudList](https://www.mudblazor.com/componentss/list) |
| `IMAGE` | `Src` | `Alt`, `Caption`,`Src` | [MudImage](https://www.mudblazor.com/components/image) | | `IMAGE` | `Src` | `Alt`, `Caption`,`Src` | [MudImage](https://www.mudblazor.com/components/image) |
| `BUTTON_GROUP` | `None` | `Variant`, `Color`, `Size`, `OverrideStyles`, `Vertical`, `DropShadow`, `Class`, `Style` | [MudButtonGroup](https://www.mudblazor.com/components/buttongroup) | | `BUTTON_GROUP` | `None` | `Variant`, `Color`, `Size`, `OverrideStyles`, `Vertical`, `DropShadow`, `Class`, `Style` | [MudButtonGroup](https://www.mudblazor.com/components/buttongroup) |
| `LAYOUT_PAPER` | `None` | `Elevation`, `Height`, `MaxHeight`, `MinHeight`, `Width`, `MaxWidth`, `MinWidth`, `IsOutlined`, `IsSquare`, `Class`, `Style` | [MudPaper](https://www.mudblazor.com/components/paper) | | `LAYOUT_PAPER` | `None` | `Elevation`, `Height`, `MaxHeight`, `MinHeight`, `Width`, `MaxWidth`, `MinWidth`, `IsOutlined`, `IsSquare`, `Class`, `Style` | [MudPaper](https://www.mudblazor.com/components/paper) |
@ -402,9 +403,9 @@ More information on rendered components can be found [here](https://www.mudblazo
- Use `Type = "SWITCH"` to render a boolean toggle. - Use `Type = "SWITCH"` to render a boolean toggle.
- Required props: - Required props:
- `Name`: unique state key used in prompt assembly and `BuildPrompt(input.fields)`. - `Name`: unique state key used in prompt assembly and `BuildPrompt(input.fields)`.
- `Label`: visible label for the switch field.
- `Value`: initial boolean state (`true` or `false`). - `Value`: initial boolean state (`true` or `false`).
- Optional props: - Optional props:
- `Label`: If set, renders the switch inside an outlines Box, otherwise renders it raw. Visible label for the switch field.
- `OnChanged`: Lua callback invoked after the switch value changes. It receives the same `input` table as `BUTTON.Action(input)` and may return `{ fields = { ... } }` to update component state. The new switch value is already reflected in `input.fields[Name]`. - `OnChanged`: Lua callback invoked after the switch value changes. It receives the same `input` table as `BUTTON.Action(input)` and may return `{ fields = { ... } }` to update component state. The new switch value is already reflected in `input.fields[Name]`.
- `Disabled`: defaults to `false`; disables user interaction while still allowing the value to be included in prompt assembly. - `Disabled`: defaults to `false`; disables user interaction while still allowing the value to be included in prompt assembly.
- `UserPrompt`: prompt context text for this field. - `UserPrompt`: prompt context text for this field.

View File

@ -114,7 +114,7 @@ ASSISTANT = {
["Type"] = "SWITCH", ["Type"] = "SWITCH",
["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>", -- Switches render mode between boxed switch and normal switch
["Value"] = true, -- initial switch state ["Value"] = true, -- initial switch state
["OnChanged"] = function(input) -- optional; same input and return contract as BUTTON.Action(input) ["OnChanged"] = function(input) -- optional; same input and return contract as BUTTON.Action(input)
return nil return nil
@ -297,13 +297,18 @@ ASSISTANT = {
{ {
["Type"] = "LINK", -- required ["Type"] = "LINK", -- required
["Text"] = "<user readable link text>", ["Text"] = "<user readable link text>",
["Href"] = "<link>" -- required ["Href"] = "<link>", -- required
["IconColor"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>",
}, },
{ {
["Type"] = "TEXT", -- required ["Type"] = "TEXT", -- required
["Text"] = "<user readable text>" ["Text"] = "<user readable text>",
["Icon"] = "Icons.Material.Filled.HorizontalRule",
["IconColor"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>",
} }
} },
["Class"] = "<optional MudBlazor or css classes>",
["Style"] = "<optional css styles>",
} }
}, },
{ {

View File

@ -53,6 +53,12 @@ internal sealed class AssistantColorPicker : AssistantComponentBase
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value); set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
} }
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class public string Class
{ {
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class)); get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));

View File

@ -60,6 +60,12 @@ internal sealed class AssistantDatePicker : AssistantComponentBase
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value); set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
} }
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class public string Class
{ {
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class)); get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));

View File

@ -66,6 +66,12 @@ internal sealed class AssistantDateRangePicker : AssistantComponentBase
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value); set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
} }
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class public string Class
{ {
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class)); get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));

View File

@ -4,5 +4,7 @@ public class AssistantListItem
{ {
public string Type { get; set; } = "TEXT"; public string Type { get; set; } = "TEXT";
public string Text { get; set; } = string.Empty; public string Text { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
public string IconColor { get; set; } = string.Empty;
public string? Href { get; set; } public string? Href { get; set; }
} }

View File

@ -66,6 +66,12 @@ internal sealed class AssistantTimePicker : AssistantComponentBase
set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value); set => AssistantComponentPropHelper.WriteString(this.Props, nameof(this.UserPrompt), value);
} }
public int Elevation
{
get => AssistantComponentPropHelper.ReadInt(this.Props, nameof(this.Elevation), 6);
set => AssistantComponentPropHelper.WriteInt(this.Props, nameof(this.Elevation), value);
}
public string Class public string Class
{ {
get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class)); get => AssistantComponentPropHelper.ReadString(this.Props, nameof(this.Class));

View File

@ -18,9 +18,9 @@ public static class ComponentPropSpecs
] ]
), ),
[AssistantComponentType.BUTTON] = new( [AssistantComponentType.BUTTON] = new(
required: ["Name", "Text", "Action"], required: ["Name", "Action"],
optional: [ optional: [
"IsIconButton", "Variant", "Color", "IsFullWidth", "Size", "Text", "IsIconButton", "Variant", "Color", "IsFullWidth", "Size",
"StartIcon", "EndIcon", "IconColor", "IconSize", "Class", "Style" "StartIcon", "EndIcon", "IconColor", "IconSize", "Class", "Style"
] ]
), ),
@ -44,10 +44,10 @@ public static class ComponentPropSpecs
optional: ["ValidationMessage", "Class", "Style"] optional: ["ValidationMessage", "Class", "Style"]
), ),
[AssistantComponentType.SWITCH] = new( [AssistantComponentType.SWITCH] = new(
required: ["Name", "Label", "Value"], required: ["Name", "Value"],
optional: [ optional: [
"OnChanged", "LabelOn", "LabelOff", "LabelPlacement", "Icon", "IconColor", "UserPrompt", "Label", "OnChanged", "LabelOn", "LabelOff", "LabelPlacement", "Icon", "IconColor",
"CheckedColor", "UncheckedColor", "Disabled", "Class", "Style", "UserPrompt", "CheckedColor", "UncheckedColor", "Disabled", "Class", "Style",
] ]
), ),
[AssistantComponentType.HEADING] = new( [AssistantComponentType.HEADING] = new(
@ -84,7 +84,7 @@ public static class ComponentPropSpecs
[AssistantComponentType.DATE_PICKER] = new( [AssistantComponentType.DATE_PICKER] = new(
required: ["Name", "Label"], required: ["Name", "Label"],
optional: [ optional: [
"Value", "Placeholder", "HelperText", "DateFormat", "Color", "Value", "Placeholder", "HelperText", "DateFormat", "Color", "Elevation",
"PickerVariant", "UserPrompt", "Class", "Style" "PickerVariant", "UserPrompt", "Class", "Style"
] ]
), ),
@ -92,14 +92,14 @@ public static class ComponentPropSpecs
required: ["Name", "Label"], required: ["Name", "Label"],
optional: [ optional: [
"Value", "PlaceholderStart", "PlaceholderEnd", "HelperText", "DateFormat", "Value", "PlaceholderStart", "PlaceholderEnd", "HelperText", "DateFormat",
"Color", "PickerVariant", "UserPrompt", "Class", "Style" "Elevation", "Color", "PickerVariant", "UserPrompt", "Class", "Style"
] ]
), ),
[AssistantComponentType.TIME_PICKER] = new( [AssistantComponentType.TIME_PICKER] = new(
required: ["Name", "Label"], required: ["Name", "Label"],
optional: [ optional: [
"Value", "Placeholder", "HelperText", "TimeFormat", "AmPm", "Color", "Value", "Placeholder", "HelperText", "TimeFormat", "AmPm", "Color",
"PickerVariant", "UserPrompt", "Class", "Style" "Elevation", "PickerVariant", "UserPrompt", "Class", "Style"
] ]
), ),
[AssistantComponentType.LAYOUT_ITEM] = new( [AssistantComponentType.LAYOUT_ITEM] = new(

View File

@ -1,6 +1,7 @@
using AIStudio.Tools.PluginSystem.Assistants.DataModel; using AIStudio.Tools.PluginSystem.Assistants.DataModel;
using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout; using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
using Lua; using Lua;
using System.Text;
namespace AIStudio.Tools.PluginSystem.Assistants; namespace AIStudio.Tools.PluginSystem.Assistants;
@ -469,8 +470,19 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
if (!table.TryGetValue("Type", out var typeVal) || !typeVal.TryRead<string>(out var type)) if (!table.TryGetValue("Type", out var typeVal) || !typeVal.TryRead<string>(out var type))
return false; return false;
table.TryGetValue("Icon", out var iconVal);
iconVal.TryRead<string>(out var icon);
icon ??= string.Empty;
table.TryGetValue("IconColor", out var iconColorVal);
iconColorVal.TryRead<string>(out var iconColor);
iconColor ??= string.Empty;
item.Text = text; item.Text = text;
item.Type = type; item.Type = type;
item.Icon = icon;
item.IconColor = iconColor;
if (table.TryGetValue("Href", out var hrefVal) && hrefVal.TryRead<string>(out var href)) if (table.TryGetValue("Href", out var hrefVal) && hrefVal.TryRead<string>(out var href))
{ {