mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-03-30 07:11:38 +00:00
advanced layout: included the layout option in the rendering and documented them
This commit is contained in:
parent
5106800399
commit
edc717cf24
@ -2,6 +2,7 @@
|
||||
@using AIStudio.Components
|
||||
@using AIStudio.Settings
|
||||
@using AIStudio.Tools.PluginSystem.Assistants.DataModel
|
||||
@using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout
|
||||
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogDynamic>
|
||||
|
||||
@foreach (var component in this.RootComponent!.Children)
|
||||
@ -133,6 +134,55 @@
|
||||
</MudButtonGroup>
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.LAYOUT_GRID:
|
||||
if (component is AssistantGrid assistantGrid)
|
||||
{
|
||||
var grid = assistantGrid;
|
||||
<MudGrid Justify="@(AssistantComponentPropHelper.GetJustify(grid.Justify) ?? Justify.FlexStart)"
|
||||
Spacing="@grid.Spacing"
|
||||
Class="@grid.Class"
|
||||
Style="@this.GetOptionalStyle(grid.Style)">
|
||||
@this.RenderChildren(grid.Children)
|
||||
</MudGrid>
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.LAYOUT_ITEM:
|
||||
if (component is AssistantItem assistantItem)
|
||||
{
|
||||
@this.RenderLayoutItem(assistantItem)
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.LAYOUT_PAPER:
|
||||
if (component is AssistantPaper assistantPaper)
|
||||
{
|
||||
var paper = assistantPaper;
|
||||
<MudPaper Elevation="@paper.Elevation"
|
||||
Outlined="@paper.IsOutlined"
|
||||
Square="@paper.IsSquare"
|
||||
Class="@paper.Class"
|
||||
Style="@this.BuildPaperStyle(paper)">
|
||||
@this.RenderChildren(paper.Children)
|
||||
</MudPaper>
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.LAYOUT_STACK:
|
||||
if (component is AssistantStack assistantStack)
|
||||
{
|
||||
var stack = assistantStack;
|
||||
<MudStack Row="@stack.IsRow"
|
||||
Reverse="@stack.IsReverse"
|
||||
Breakpoint="@AssistantComponentPropHelper.GetBreakpoint(stack.Breakpoint, Breakpoint.None)"
|
||||
AlignItems="@(AssistantComponentPropHelper.GetAlignment(stack.Align) ?? AlignItems.Stretch)"
|
||||
Justify="@(AssistantComponentPropHelper.GetJustify(stack.Justify) ?? Justify.FlexStart)"
|
||||
StretchItems="@(AssistantComponentPropHelper.GetStretching(stack.Stretch) ?? StretchItems.None)"
|
||||
Wrap="@(AssistantComponentPropHelper.GetWrap(stack.Wrap) ?? Wrap.Wrap)"
|
||||
Spacing="@stack.Spacing"
|
||||
Class="@stack.Class"
|
||||
Style="@this.GetOptionalStyle(this.GetRawStyle(stack))">
|
||||
@this.RenderChildren(stack.Children)
|
||||
</MudStack>
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.PROVIDER_SELECTION:
|
||||
if (component is AssistantProviderSelection providerSelection)
|
||||
{
|
||||
@ -244,4 +294,70 @@
|
||||
break;
|
||||
}
|
||||
</text>;
|
||||
|
||||
private string? GetRawStyle(IAssistantComponent component)
|
||||
{
|
||||
if (!component.Props.TryGetValue("Style", out var rawStyle) || rawStyle is null)
|
||||
return null;
|
||||
|
||||
return rawStyle as string ?? rawStyle.ToString();
|
||||
}
|
||||
|
||||
private string? BuildPaperStyle(AssistantPaper paper)
|
||||
{
|
||||
List<string> styles = [];
|
||||
|
||||
this.AddStyle(styles, "height", paper.Height);
|
||||
this.AddStyle(styles, "max-height", paper.MaxHeight);
|
||||
this.AddStyle(styles, "min-height", paper.MinHeight);
|
||||
this.AddStyle(styles, "width", paper.Width);
|
||||
this.AddStyle(styles, "max-width", paper.MaxWidth);
|
||||
this.AddStyle(styles, "min-width", paper.MinWidth);
|
||||
|
||||
var customStyle = this.GetRawStyle(paper);
|
||||
if (!string.IsNullOrWhiteSpace(customStyle))
|
||||
styles.Add(customStyle.Trim().TrimEnd(';'));
|
||||
|
||||
return styles.Count == 0 ? null : string.Join("; ", styles);
|
||||
}
|
||||
|
||||
private RenderFragment RenderLayoutItem(AssistantItem item) => builder =>
|
||||
{
|
||||
builder.OpenComponent<MudItem>(0);
|
||||
|
||||
if (item.Xs.HasValue)
|
||||
builder.AddAttribute(1, "xs", item.Xs.Value);
|
||||
|
||||
if (item.Sm.HasValue)
|
||||
builder.AddAttribute(2, "sm", item.Sm.Value);
|
||||
|
||||
if (item.Md.HasValue)
|
||||
builder.AddAttribute(3, "md", item.Md.Value);
|
||||
|
||||
if (item.Lg.HasValue)
|
||||
builder.AddAttribute(4, "lg", item.Lg.Value);
|
||||
|
||||
if (item.Xl.HasValue)
|
||||
builder.AddAttribute(5, "xl", item.Xl.Value);
|
||||
|
||||
if (item.Xxl.HasValue)
|
||||
builder.AddAttribute(6, "xxl", item.Xxl.Value);
|
||||
|
||||
var itemClass = item.Class;
|
||||
if (!string.IsNullOrWhiteSpace(itemClass))
|
||||
builder.AddAttribute(7, nameof(MudItem.Class), itemClass);
|
||||
|
||||
var itemStyle = this.GetOptionalStyle(item.Style);
|
||||
if (!string.IsNullOrWhiteSpace(itemStyle))
|
||||
builder.AddAttribute(8, nameof(MudItem.Style), itemStyle);
|
||||
|
||||
builder.AddAttribute(9, nameof(MudItem.ChildContent), this.RenderChildren(item.Children));
|
||||
builder.CloseComponent();
|
||||
};
|
||||
|
||||
private void AddStyle(List<string> styles, string key, string value)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
styles.Add($"{key}: {value.Trim().TrimEnd(';')}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,52 +76,7 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
||||
var rootComponent = this.RootComponent;
|
||||
if (rootComponent is not null)
|
||||
{
|
||||
foreach (var component in rootComponent.Children)
|
||||
{
|
||||
switch (component.Type)
|
||||
{
|
||||
case AssistantComponentType.TEXT_AREA:
|
||||
if (component is AssistantTextArea textArea)
|
||||
{
|
||||
this.inputFields.Add(textArea.Name, textArea.PrefillText);
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.DROPDOWN:
|
||||
if (component is AssistantDropdown dropdown)
|
||||
{
|
||||
this.dropdownFields.Add(dropdown.Name, dropdown.Default.Value);
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.SWITCH:
|
||||
if (component is AssistantSwitch switchComponent)
|
||||
{
|
||||
this.switchFields.Add(switchComponent.Name, switchComponent.Value);
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.WEB_CONTENT_READER:
|
||||
if (component is AssistantWebContentReader webContent)
|
||||
{
|
||||
this.webContentFields.Add(webContent.Name, new WebContentState
|
||||
{
|
||||
Preselect = webContent.Preselect,
|
||||
PreselectContentCleanerAgent = webContent.PreselectContentCleanerAgent,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.FILE_CONTENT_READER:
|
||||
if (component is AssistantFileContentReader fileContent)
|
||||
{
|
||||
this.fileContentFields.Add(fileContent.Name, new FileContentState());
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.COLOR_PICKER:
|
||||
if (component is AssistantColorPicker assistantColorPicker)
|
||||
{
|
||||
this.colorPickerFields.Add(assistantColorPicker.Name, assistantColorPicker.Placeholder);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.InitializeComponentState(rootComponent.Children);
|
||||
}
|
||||
|
||||
base.OnInitialized();
|
||||
@ -277,32 +232,7 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
||||
var meta = new LuaTable();
|
||||
var rootComponent = this.RootComponent;
|
||||
if (rootComponent is not null)
|
||||
{
|
||||
foreach (var component in rootComponent.Children)
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case AssistantTextArea textArea:
|
||||
this.AddMetaEntry(meta, textArea.Name, component.Type, textArea.Label, textArea.UserPrompt);
|
||||
break;
|
||||
case AssistantDropdown dropdown:
|
||||
this.AddMetaEntry(meta, dropdown.Name, component.Type, dropdown.Label, dropdown.UserPrompt);
|
||||
break;
|
||||
case AssistantSwitch switchComponent:
|
||||
this.AddMetaEntry(meta, switchComponent.Name, component.Type, switchComponent.Label, switchComponent.UserPrompt);
|
||||
break;
|
||||
case AssistantWebContentReader webContent:
|
||||
this.AddMetaEntry(meta, webContent.Name, component.Type, null, webContent.UserPrompt);
|
||||
break;
|
||||
case AssistantFileContentReader fileContent:
|
||||
this.AddMetaEntry(meta, fileContent.Name, component.Type, null, fileContent.UserPrompt);
|
||||
break;
|
||||
case AssistantColorPicker colorPicker:
|
||||
this.AddMetaEntry(meta, colorPicker.Name, component.Type, colorPicker.Label, colorPicker.UserPrompt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.AddMetaEntries(meta, rootComponent.Children);
|
||||
|
||||
input["meta"] = meta;
|
||||
|
||||
@ -343,89 +273,50 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
||||
if (rootComponent is null)
|
||||
return prompt;
|
||||
|
||||
foreach (var component in rootComponent.Children)
|
||||
return this.CollectUserPromptFallback(rootComponent.Children);
|
||||
}
|
||||
|
||||
private void InitializeComponentState(IEnumerable<IAssistantComponent> components)
|
||||
{
|
||||
foreach (var component in components)
|
||||
{
|
||||
var userInput = string.Empty;
|
||||
var userDecision = false;
|
||||
switch (component.Type)
|
||||
{
|
||||
case AssistantComponentType.TEXT_AREA:
|
||||
if (component is AssistantTextArea textArea)
|
||||
{
|
||||
prompt += $"context:{Environment.NewLine}{textArea.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
if (this.inputFields.TryGetValue(textArea.Name, out userInput))
|
||||
{
|
||||
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
||||
}
|
||||
}
|
||||
if (component is AssistantTextArea textArea && !this.inputFields.ContainsKey(textArea.Name))
|
||||
this.inputFields.Add(textArea.Name, textArea.PrefillText);
|
||||
break;
|
||||
case AssistantComponentType.DROPDOWN:
|
||||
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))
|
||||
{
|
||||
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
||||
}
|
||||
}
|
||||
if (component is AssistantDropdown dropdown && !this.dropdownFields.ContainsKey(dropdown.Name))
|
||||
this.dropdownFields.Add(dropdown.Name, dropdown.Default.Value);
|
||||
break;
|
||||
case AssistantComponentType.SWITCH:
|
||||
if (component is AssistantSwitch switchComponent)
|
||||
{
|
||||
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{switchComponent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
if (this.switchFields.TryGetValue(switchComponent.Name, out userDecision))
|
||||
{
|
||||
prompt += $"user decision:{Environment.NewLine}{userDecision}";
|
||||
}
|
||||
}
|
||||
if (component is AssistantSwitch switchComponent && !this.switchFields.ContainsKey(switchComponent.Name))
|
||||
this.switchFields.Add(switchComponent.Name, switchComponent.Value);
|
||||
break;
|
||||
case AssistantComponentType.WEB_CONTENT_READER:
|
||||
if (component is AssistantWebContentReader webContent &&
|
||||
this.webContentFields.TryGetValue(webContent.Name, out var webState))
|
||||
if (component is AssistantWebContentReader webContent && !this.webContentFields.ContainsKey(webContent.Name))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(webContent.UserPrompt))
|
||||
this.webContentFields.Add(webContent.Name, new WebContentState
|
||||
{
|
||||
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{webContent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(webState.Content))
|
||||
{
|
||||
prompt += $"user prompt:{Environment.NewLine}{webState.Content}";
|
||||
}
|
||||
Preselect = webContent.Preselect,
|
||||
PreselectContentCleanerAgent = webContent.PreselectContentCleanerAgent,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.FILE_CONTENT_READER:
|
||||
if (component is AssistantFileContentReader fileContent &&
|
||||
this.fileContentFields.TryGetValue(fileContent.Name, out var fileState))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(fileContent.UserPrompt))
|
||||
{
|
||||
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{fileContent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fileState.Content))
|
||||
{
|
||||
prompt += $"user prompt:{Environment.NewLine}{fileState.Content}";
|
||||
}
|
||||
}
|
||||
if (component is AssistantFileContentReader fileContent && !this.fileContentFields.ContainsKey(fileContent.Name))
|
||||
this.fileContentFields.Add(fileContent.Name, new FileContentState());
|
||||
break;
|
||||
case AssistantComponentType.COLOR_PICKER:
|
||||
if (component is AssistantColorPicker colorPicker)
|
||||
{
|
||||
prompt += $"context:{Environment.NewLine}{colorPicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
if (this.inputFields.TryGetValue(colorPicker.Name, out userInput))
|
||||
{
|
||||
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
prompt += $"{userInput}{Environment.NewLine}";
|
||||
if (component is AssistantColorPicker assistantColorPicker && !this.colorPickerFields.ContainsKey(assistantColorPicker.Name))
|
||||
this.colorPickerFields.Add(assistantColorPicker.Name, assistantColorPicker.Placeholder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return prompt;
|
||||
if (component.Children.Count > 0)
|
||||
this.InitializeComponentState(component.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private static string MergeClass(string customClass, string fallback)
|
||||
@ -571,4 +462,109 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
||||
var time = this.AddUserRequest(await this.CollectUserPromptAsync());
|
||||
await this.AddAIResponseAsync(time);
|
||||
}
|
||||
|
||||
private void AddMetaEntries(LuaTable meta, IEnumerable<IAssistantComponent> components)
|
||||
{
|
||||
foreach (var component in components)
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case AssistantTextArea textArea:
|
||||
this.AddMetaEntry(meta, textArea.Name, component.Type, textArea.Label, textArea.UserPrompt);
|
||||
break;
|
||||
case AssistantDropdown dropdown:
|
||||
this.AddMetaEntry(meta, dropdown.Name, component.Type, dropdown.Label, dropdown.UserPrompt);
|
||||
break;
|
||||
case AssistantSwitch switchComponent:
|
||||
this.AddMetaEntry(meta, switchComponent.Name, component.Type, switchComponent.Label, switchComponent.UserPrompt);
|
||||
break;
|
||||
case AssistantWebContentReader webContent:
|
||||
this.AddMetaEntry(meta, webContent.Name, component.Type, null, webContent.UserPrompt);
|
||||
break;
|
||||
case AssistantFileContentReader fileContent:
|
||||
this.AddMetaEntry(meta, fileContent.Name, component.Type, null, fileContent.UserPrompt);
|
||||
break;
|
||||
case AssistantColorPicker colorPicker:
|
||||
this.AddMetaEntry(meta, colorPicker.Name, component.Type, colorPicker.Label, colorPicker.UserPrompt);
|
||||
break;
|
||||
}
|
||||
|
||||
if (component.Children.Count > 0)
|
||||
this.AddMetaEntries(meta, component.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private string CollectUserPromptFallback(IEnumerable<IAssistantComponent> components)
|
||||
{
|
||||
var prompt = string.Empty;
|
||||
|
||||
foreach (var component in components)
|
||||
{
|
||||
var userInput = string.Empty;
|
||||
var userDecision = false;
|
||||
|
||||
switch (component.Type)
|
||||
{
|
||||
case AssistantComponentType.TEXT_AREA:
|
||||
if (component is AssistantTextArea textArea)
|
||||
{
|
||||
prompt += $"context:{Environment.NewLine}{textArea.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
if (this.inputFields.TryGetValue(textArea.Name, out userInput))
|
||||
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.DROPDOWN:
|
||||
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))
|
||||
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.SWITCH:
|
||||
if (component is AssistantSwitch switchComponent)
|
||||
{
|
||||
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{switchComponent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
if (this.switchFields.TryGetValue(switchComponent.Name, out userDecision))
|
||||
prompt += $"user decision:{Environment.NewLine}{userDecision}";
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.WEB_CONTENT_READER:
|
||||
if (component is AssistantWebContentReader webContent &&
|
||||
this.webContentFields.TryGetValue(webContent.Name, out var webState))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(webContent.UserPrompt))
|
||||
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{webContent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(webState.Content))
|
||||
prompt += $"user prompt:{Environment.NewLine}{webState.Content}";
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.FILE_CONTENT_READER:
|
||||
if (component is AssistantFileContentReader fileContent &&
|
||||
this.fileContentFields.TryGetValue(fileContent.Name, out var fileState))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(fileContent.UserPrompt))
|
||||
prompt += $"{Environment.NewLine}context:{Environment.NewLine}{fileContent.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fileState.Content))
|
||||
prompt += $"user prompt:{Environment.NewLine}{fileState.Content}";
|
||||
}
|
||||
break;
|
||||
case AssistantComponentType.COLOR_PICKER:
|
||||
if (component is AssistantColorPicker colorPicker)
|
||||
{
|
||||
prompt += $"context:{Environment.NewLine}{colorPicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
|
||||
if (this.colorPickerFields.TryGetValue(colorPicker.Name, out userInput))
|
||||
prompt += $"user prompt:{Environment.NewLine}{userInput}";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (component.Children.Count > 0)
|
||||
prompt += this.CollectUserPromptFallback(component.Children);
|
||||
}
|
||||
|
||||
return prompt;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,10 @@ Supported types (matching the Blazor UI components):
|
||||
- `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`.
|
||||
- `LAYOUT_GRID`: renders a `MudGrid`; `Children` must contain only `LAYOUT_ITEM` components and `Props` may include `Justify`, `Spacing`, `Class`, `Style`.
|
||||
- `LAYOUT_ITEM`: renders a `MudItem`; use it inside `LAYOUT_GRID` and configure breakpoints with `Xs`, `Sm`, `Md`, `Lg`, `Xl`, `Xxl`, plus optional `Class`, `Style`.
|
||||
- `LAYOUT_PAPER`: renders a `MudPaper`; may include `Elevation`, `Height`, `MaxHeight`, `MinHeight`, `Width`, `MaxWidth`, `MinWidth`, `IsOutlined`, `IsSquare`, `Class`, `Style`.
|
||||
- `LAYOUT_STACK`: renders a `MudStack`; may include `IsRow`, `IsReverse`, `Breakpoint`, `Align`, `Justify`, `Stretch`, `Wrap`, `Spacing`, `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.
|
||||
@ -24,7 +28,7 @@ Supported types (matching the Blazor UI components):
|
||||
Images referenced via the `plugin://` scheme must exist in the plugin directory (e.g., `assets/example.png`). Drop the file there and point `Src` at it. The component will read the file at runtime, encode it as Base64, and render it inside the assistant UI.
|
||||
|
||||
## Prompt Assembly
|
||||
Each component exposes a `UserPrompt` string. When the assistant runs, `AssistantDynamic` iterates over `RootComponent.Children` and, for each component that has a prompt, emits:
|
||||
Each component exposes a `UserPrompt` string. When the assistant runs, `AssistantDynamic` recursively iterates over the component tree and, for each component that has a prompt, emits:
|
||||
|
||||
```
|
||||
context:
|
||||
@ -280,6 +284,125 @@ Example:
|
||||
}
|
||||
```
|
||||
|
||||
### `LAYOUT_GRID` reference
|
||||
- Use `Type = "LAYOUT_GRID"` to render a MudBlazor grid container.
|
||||
- Required props:
|
||||
- `Name`: unique identifier for the layout node.
|
||||
- Required structure:
|
||||
- `Children`: array of `LAYOUT_ITEM` component tables. Other child component types are ignored.
|
||||
- Optional props:
|
||||
- `Justify`: one of the MudBlazor `Justify` enum names such as `FlexStart`, `Center`, `SpaceBetween`; omitted values fall back to `FlexStart`.
|
||||
- `Spacing`: integer spacing between grid items; omitted values fall back to `6`.
|
||||
- `Class`, `Style`: forwarded to the rendered `MudGrid` for layout/styling.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
{
|
||||
["Type"] = "LAYOUT_GRID",
|
||||
["Props"] = {
|
||||
["Name"] = "mainGrid",
|
||||
["Justify"] = "FlexStart",
|
||||
["Spacing"] = 2
|
||||
},
|
||||
["Children"] = {
|
||||
{
|
||||
["Type"] = "LAYOUT_ITEM",
|
||||
["Props"] = {
|
||||
["Name"] = "leftItem",
|
||||
["Xs"] = 12,
|
||||
["Md"] = 6
|
||||
}
|
||||
},
|
||||
{
|
||||
["Type"] = "LAYOUT_ITEM",
|
||||
["Props"] = {
|
||||
["Name"] = "rightItem",
|
||||
["Xs"] = 12,
|
||||
["Md"] = 6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `LAYOUT_ITEM` reference
|
||||
- Use `Type = "LAYOUT_ITEM"` to render a MudBlazor grid item.
|
||||
- Required props:
|
||||
- `Name`: unique identifier for the layout node.
|
||||
- Intended parent:
|
||||
- Use this component inside `LAYOUT_GRID`.
|
||||
- Optional props:
|
||||
- `Xs`, `Sm`, `Md`, `Lg`, `Xl`, `Xxl`: integer breakpoint widths. Omit a breakpoint to leave it unset.
|
||||
- `Class`, `Style`: forwarded to the rendered `MudItem` for layout/styling.
|
||||
- `Children` may contain any other assistant components you want to place inside the item.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
{
|
||||
["Type"] = "LAYOUT_ITEM",
|
||||
["Props"] = {
|
||||
["Name"] = "contentColumn",
|
||||
["Xs"] = 12,
|
||||
["Lg"] = 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `LAYOUT_PAPER` reference
|
||||
- Use `Type = "LAYOUT_PAPER"` to render a MudBlazor paper container.
|
||||
- Required props:
|
||||
- `Name`: unique identifier for the layout node.
|
||||
- Optional props:
|
||||
- `Elevation`: integer elevation; omitted values fall back to `1`.
|
||||
- `Height`, `MaxHeight`, `MinHeight`, `Width`, `MaxWidth`, `MinWidth`: CSS size values such as `100%`, `24rem`, `50vh`.
|
||||
- `IsOutlined`: defaults to `false`; toggles outlined mode.
|
||||
- `IsSquare`: defaults to `false`; removes rounded corners.
|
||||
- `Class`, `Style`: forwarded to the rendered `MudPaper` for layout/styling.
|
||||
- `Children` may contain any other assistant components you want to wrap.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
{
|
||||
["Type"] = "LAYOUT_PAPER",
|
||||
["Props"] = {
|
||||
["Name"] = "contentPaper",
|
||||
["Elevation"] = 2,
|
||||
["Width"] = "100%",
|
||||
["IsOutlined"] = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `LAYOUT_STACK` reference
|
||||
- Use `Type = "LAYOUT_STACK"` to render a MudBlazor stack layout.
|
||||
- Required props:
|
||||
- `Name`: unique identifier for the layout node.
|
||||
- Optional props:
|
||||
- `IsRow`: defaults to `false`; renders items horizontally.
|
||||
- `IsReverse`: defaults to `false`; reverses the visual order.
|
||||
- `Breakpoint`: one of the MudBlazor `Breakpoint` enum names such as `Sm`, `Md`, `Lg`; omitted values fall back to `None`.
|
||||
- `Align`: one of the MudBlazor `AlignItems` enum names such as `Start`, `Center`, `Stretch`; omitted values fall back to `Stretch`.
|
||||
- `Justify`: one of the MudBlazor `Justify` enum names such as `FlexStart`, `Center`, `SpaceBetween`; omitted values fall back to `FlexStart`.
|
||||
- `Stretch`: one of the MudBlazor `StretchItems` enum names such as `None`, `Start`, `End`, `Stretch`; omitted values fall back to `None`.
|
||||
- `Wrap`: one of the MudBlazor `Wrap` enum names such as `Wrap`, `NoWrap`, `WrapReverse`; omitted values fall back to `Wrap`.
|
||||
- `Spacing`: integer spacing between child components; omitted values fall back to `3`.
|
||||
- `Class`, `Style`: forwarded to the rendered `MudStack` for layout/styling.
|
||||
- `Children` may contain any other assistant components you want to arrange.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
{
|
||||
["Type"] = "LAYOUT_STACK",
|
||||
["Props"] = {
|
||||
["Name"] = "toolbarRow",
|
||||
["IsRow"] = true,
|
||||
["Align"] = "Center",
|
||||
["Justify"] = "SpaceBetween",
|
||||
["Spacing"] = 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `SWITCH` reference
|
||||
- Use `Type = "SWITCH"` to render a boolean toggle.
|
||||
- Required props:
|
||||
|
||||
@ -168,6 +168,48 @@ ASSISTANT = {
|
||||
-- BUTTON_ELEMENTS
|
||||
}
|
||||
},
|
||||
{
|
||||
["Type"] = "LAYOUT_STACK",
|
||||
["Props"] = {
|
||||
["Name"] = "exampleStack",
|
||||
["IsRow"] = true,
|
||||
["Align"] = "Center",
|
||||
["Justify"] = "SpaceBetween",
|
||||
["Wrap"] = "Wrap",
|
||||
["Spacing"] = 2,
|
||||
["Class"] = "<optional MudBlazor or css classes>",
|
||||
["Style"] = "<optional css styles>",
|
||||
},
|
||||
["Children"] = {
|
||||
-- CHILDREN
|
||||
}
|
||||
},
|
||||
{
|
||||
["Type"] = "LAYOUT_PAPER",
|
||||
["Props"] = {
|
||||
["Name"] = "examplePaper",
|
||||
["Elevation"] = 2,
|
||||
["Width"] = "100%",
|
||||
["Class"] = "pa-4 mb-3",
|
||||
["Style"] = "<optional css styles>",
|
||||
},
|
||||
["Children"] = {
|
||||
-- CHILDREN
|
||||
}
|
||||
},
|
||||
{
|
||||
["Type"] = "LAYOUT_GRID",
|
||||
["Props"] = {
|
||||
["Name"] = "exampleGrid",
|
||||
["Justify"] = "FlexStart",
|
||||
["Spacing"] = 2,
|
||||
["Class"] = "<optional MudBlazor or css classes>",
|
||||
["Style"] = "<optional css styles>",
|
||||
},
|
||||
["Children"] = {
|
||||
-- CHILDREN
|
||||
}
|
||||
},
|
||||
{
|
||||
["Type"] = "PROVIDER_SELECTION", -- required
|
||||
["Props"] = {
|
||||
|
||||
@ -89,7 +89,7 @@ public static class ComponentPropSpecs
|
||||
[AssistantComponentType.LAYOUT_PAPER] = new(
|
||||
required: ["Name"],
|
||||
optional: [
|
||||
"Height", "MaxHeight", "MinHeight", "Width", "MaxWidth", "MinWidth",
|
||||
"Elevation", "Height", "MaxHeight", "MinHeight", "Width", "MaxWidth", "MinWidth",
|
||||
"IsOutlined", "IsSquare", "Class", "Style"
|
||||
]
|
||||
),
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
|
||||
using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
|
||||
using Lua;
|
||||
|
||||
namespace AIStudio.Tools.PluginSystem.Assistants;
|
||||
@ -298,6 +299,16 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
||||
}
|
||||
}
|
||||
|
||||
if (component is AssistantGrid grid)
|
||||
{
|
||||
var invalidChildren = grid.Children.Where(child => child.Type != AssistantComponentType.LAYOUT_ITEM).ToList();
|
||||
if (invalidChildren.Count > 0)
|
||||
{
|
||||
LOGGER.LogWarning("Assistant plugin '{PluginName}' LAYOUT_GRID contains non-LAYOUT_ITEM children. Only LAYOUT_ITEM children are supported and invalid children are ignored.", this.Name);
|
||||
grid.Children = grid.Children.Where(child => child.Type == AssistantComponentType.LAYOUT_ITEM).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user