mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-03-29 13:51:37 +00:00
renewed callback contract and made live value updates from all writeable props and values possible
This commit is contained in:
parent
82099a0677
commit
d1ece556a6
@ -194,9 +194,9 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
|
|
||||||
private LuaTable BuildPromptInput()
|
private LuaTable BuildPromptInput()
|
||||||
{
|
{
|
||||||
var input = new LuaTable();
|
var state = new LuaTable();
|
||||||
var rootComponent = this.RootComponent;
|
var rootComponent = this.RootComponent;
|
||||||
input["state"] = rootComponent is not null
|
state = rootComponent is not null
|
||||||
? this.assistantState.ToLuaTable(rootComponent.Children)
|
? this.assistantState.ToLuaTable(rootComponent.Children)
|
||||||
: new LuaTable();
|
: new LuaTable();
|
||||||
|
|
||||||
@ -207,9 +207,9 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
["Actions"] = this.currentProfile.Actions,
|
["Actions"] = this.currentProfile.Actions,
|
||||||
["Num"] = this.currentProfile.Num,
|
["Num"] = this.currentProfile.Num,
|
||||||
};
|
};
|
||||||
input["profile"] = profile;
|
state["profile"] = profile;
|
||||||
|
|
||||||
return input;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CollectUserPromptFallback()
|
private string CollectUserPromptFallback()
|
||||||
@ -308,24 +308,54 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
|
|
||||||
private void ApplyActionResult(LuaTable result, AssistantComponentType sourceType)
|
private void ApplyActionResult(LuaTable result, AssistantComponentType sourceType)
|
||||||
{
|
{
|
||||||
if (!result.TryGetValue("fields", out var fieldsValue))
|
if (!result.TryGetValue("state", out var statesValue))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!fieldsValue.TryRead<LuaTable>(out var fieldsTable))
|
if (!statesValue.TryRead<LuaTable>(out var stateTable))
|
||||||
{
|
{
|
||||||
this.Logger.LogWarning("Assistant {ComponentType} callback returned a non-table 'fields' value. The result is ignored.", sourceType);
|
this.Logger.LogWarning($"Assistant {sourceType} callback returned a non-table 'state' value. The result is ignored.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var pair in fieldsTable)
|
foreach (var component in stateTable)
|
||||||
{
|
{
|
||||||
if (!pair.Key.TryRead<string>(out var fieldName) || string.IsNullOrWhiteSpace(fieldName))
|
if (!component.Key.TryRead<string>(out var componentName) || string.IsNullOrWhiteSpace(componentName))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
this.TryApplyFieldUpdate(fieldName, pair.Value, sourceType);
|
if (!component.Value.TryRead<LuaTable>(out var componentUpdate))
|
||||||
|
{
|
||||||
|
this.Logger.LogWarning($"Assistant {sourceType} callback returned a non-table update for '{componentName}'. The result is ignored.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.TryApplyComponentUpdate(componentName, componentUpdate, sourceType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TryApplyComponentUpdate(string componentName, LuaTable componentUpdate, AssistantComponentType sourceType)
|
||||||
|
{
|
||||||
|
if (componentUpdate.TryGetValue("Value", out var value))
|
||||||
|
this.TryApplyFieldUpdate(componentName, value, sourceType);
|
||||||
|
|
||||||
|
if (!componentUpdate.TryGetValue("Props", out var propsValue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!propsValue.TryRead<LuaTable>(out var propsTable))
|
||||||
|
{
|
||||||
|
this.Logger.LogWarning($"Assistant {sourceType} callback returned a non-table 'Props' value for '{componentName}'. The props update is ignored.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootComponent = this.RootComponent;
|
||||||
|
if (rootComponent is null || !TryFindNamedComponent(rootComponent.Children, componentName, out var component))
|
||||||
|
{
|
||||||
|
this.Logger.LogWarning($"Assistant {sourceType} callback tried to update props of unknown component '{componentName}'. The props update is ignored.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ApplyPropUpdates(component, propsTable, sourceType);
|
||||||
|
}
|
||||||
|
|
||||||
private void TryApplyFieldUpdate(string fieldName, LuaValue value, AssistantComponentType sourceType)
|
private void TryApplyFieldUpdate(string fieldName, LuaValue value, AssistantComponentType sourceType)
|
||||||
{
|
{
|
||||||
if (this.assistantState.TryApplyValue(fieldName, value, out var expectedType))
|
if (this.assistantState.TryApplyValue(fieldName, value, out var expectedType))
|
||||||
@ -340,6 +370,51 @@ public partial class AssistantDynamic : AssistantBaseCore<SettingsDialogDynamic>
|
|||||||
this.Logger.LogWarning($"Assistant {sourceType} callback tried to update unknown field '{fieldName}'. The value is ignored.");
|
this.Logger.LogWarning($"Assistant {sourceType} callback tried to update unknown field '{fieldName}'. The value is ignored.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyPropUpdates(IAssistantComponent component, LuaTable propsTable, AssistantComponentType sourceType)
|
||||||
|
{
|
||||||
|
var propSpec = ComponentPropSpecs.SPECS.GetValueOrDefault(component.Type);
|
||||||
|
|
||||||
|
foreach (var prop in propsTable)
|
||||||
|
{
|
||||||
|
if (!prop.Key.TryRead<string>(out var propName) || string.IsNullOrWhiteSpace(propName))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (propSpec is not null && propSpec.NonWriteable.Contains(propName, StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
this.Logger.LogWarning($"Assistant {sourceType} callback tried to update non-writeable prop '{propName}' on component '{GetComponentName(component)}'. The value is ignored.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AssistantLuaConversion.TryReadScalarOrStructuredValue(prop.Value, out var convertedValue))
|
||||||
|
{
|
||||||
|
this.Logger.LogWarning($"Assistant {sourceType} callback returned an unsupported value for prop '{propName}' on component '{GetComponentName(component)}'. The props update is ignored.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.Props[propName] = convertedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryFindNamedComponent(IEnumerable<IAssistantComponent> components, string componentName, out IAssistantComponent component)
|
||||||
|
{
|
||||||
|
foreach (var candidate in components)
|
||||||
|
{
|
||||||
|
if (candidate is INamedAssistantComponent named && string.Equals(named.Name, componentName, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
component = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate.Children.Count > 0 && TryFindNamedComponent(candidate.Children, componentName, out component))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
component = null!;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetComponentName(IAssistantComponent component) => component is INamedAssistantComponent named ? named.Name : component.Type.ToString();
|
||||||
|
|
||||||
private EventCallback<HashSet<string>> CreateMultiselectDropdownChangedCallback(string fieldName) =>
|
private EventCallback<HashSet<string>> CreateMultiselectDropdownChangedCallback(string fieldName) =>
|
||||||
EventCallback.Factory.Create<HashSet<string>>(this, values =>
|
EventCallback.Factory.Create<HashSet<string>>(this, values =>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -24,9 +24,9 @@ This folder keeps the Lua manifest (`plugin.lua`) that defines a custom assistan
|
|||||||
- [Advanced Prompt Assembly - BuildPrompt()](#advanced-prompt-assembly---buildprompt)
|
- [Advanced Prompt Assembly - BuildPrompt()](#advanced-prompt-assembly---buildprompt)
|
||||||
- [Interface](#interface)
|
- [Interface](#interface)
|
||||||
- [`input` table shape](#input-table-shape)
|
- [`input` table shape](#input-table-shape)
|
||||||
- [Using `meta` inside BuildPrompt](#using-meta-inside-buildprompt)
|
- [Using component metadata inside BuildPrompt](#using-component-metadata-inside-buildprompt)
|
||||||
- [Example: iterate all fields with labels and include their values](#example-iterate-all-fields-with-labels-and-include-their-values)
|
- [Example: build a prompt from two fields](#example-build-a-prompt-from-two-fields)
|
||||||
- [Example: handle types differently](#example-handle-types-differently)
|
- [Example: reuse a label from `Props`](#example-reuse-a-label-from-props)
|
||||||
- [Using `profile` inside BuildPrompt](#using-profile-inside-buildprompt)
|
- [Using `profile` inside BuildPrompt](#using-profile-inside-buildprompt)
|
||||||
- [Example: Add user profile context to the prompt](#example-add-user-profile-context-to-the-prompt)
|
- [Example: Add user profile context to the prompt](#example-add-user-profile-context-to-the-prompt)
|
||||||
- [Advanced Layout Options](#advanced-layout-options)
|
- [Advanced Layout Options](#advanced-layout-options)
|
||||||
@ -147,7 +147,7 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
### `TEXT_AREA` reference
|
### `TEXT_AREA` reference
|
||||||
- Use `Type = "TEXT_AREA"` to render a MudBlazor text input or textarea.
|
- Use `Type = "TEXT_AREA"` to render a MudBlazor text input or textarea.
|
||||||
- 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)`.
|
||||||
- `Label`: visible field label.
|
- `Label`: visible field label.
|
||||||
- Optional props:
|
- Optional props:
|
||||||
- `HelperText`: helper text rendered below the input.
|
- `HelperText`: helper text rendered below the input.
|
||||||
@ -190,7 +190,7 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
### `DROPDOWN` reference
|
### `DROPDOWN` reference
|
||||||
- Use `Type = "DROPDOWN"` to render a MudBlazor select field.
|
- Use `Type = "DROPDOWN"` to render a MudBlazor select field.
|
||||||
- Required props:
|
- Required props:
|
||||||
- `Name`: unique state key used in prompt assembly, button actions, and `BuildPrompt(input.fields)`.
|
- `Name`: unique state key used in prompt assembly, button actions, and `BuildPrompt(input)`.
|
||||||
- `Label`: visible field label.
|
- `Label`: visible field label.
|
||||||
- `Default`: dropdown item table with the shape `{ ["Value"] = "<internal value>", ["Display"] = "<visible label>" }`.
|
- `Default`: dropdown item table with the shape `{ ["Value"] = "<internal value>", ["Display"] = "<visible label>" }`.
|
||||||
- `Items`: array of dropdown item tables with the same shape as `Default`.
|
- `Items`: array of dropdown item tables with the same shape as `Default`.
|
||||||
@ -211,8 +211,8 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
- `Value`: the internal raw value stored in component state and passed to prompt building.
|
- `Value`: the internal raw value stored in component state and passed to prompt building.
|
||||||
- `Display`: the visible label shown to the user in the menu and selection text.
|
- `Display`: the visible label shown to the user in the menu and selection text.
|
||||||
- Behavior notes:
|
- Behavior notes:
|
||||||
- For single-select dropdowns, `input.fields.<Name>` is a single raw value such as `germany`.
|
- For single-select dropdowns, `input.<Name>.Value` is a single raw value such as `germany`.
|
||||||
- For multiselect dropdowns, `input.fields.<Name>` is an array-like Lua table of raw values.
|
- For multiselect dropdowns, `input.<Name>.Value` is an array-like Lua table of raw values.
|
||||||
- The UI shows the `Display` text, while prompt assembly and `BuildPrompt(input)` receive the raw `Value`.
|
- The UI shows the `Display` text, while prompt assembly and `BuildPrompt(input)` receive the raw `Value`.
|
||||||
- `Default` should usually also exist in `Items`. If it is missing there, the runtime currently still renders it as an available option.
|
- `Default` should usually also exist in `Items`. If it is missing there, the runtime currently still renders it as an available option.
|
||||||
|
|
||||||
@ -272,13 +272,20 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
#### `Action(input)` interface
|
#### `Action(input)` interface
|
||||||
- The function receives the same `input` structure as `ASSISTANT.BuildPrompt(input)`.
|
- The function receives the same `input` structure as `ASSISTANT.BuildPrompt(input)`.
|
||||||
- Return `nil` for no state update.
|
- Return `nil` for no state update.
|
||||||
- To update component state, return a table with a `fields` table.
|
- Each named component is available as `input.<Name>` and exposes:
|
||||||
- `fields` keys must reference existing component `Name` values.
|
- `Type`: component type such as `TEXT_AREA` or `SWITCH`
|
||||||
- Supported write targets:
|
- `Value`: current component value
|
||||||
|
- `Props`: readable component props
|
||||||
|
- To update component state, return a table with a `state` table.
|
||||||
|
- `state` keys must reference existing component `Name` values.
|
||||||
|
- Each component update may include:
|
||||||
|
- `Value`: updates the current state value
|
||||||
|
- `Props`: partial prop updates for writable props
|
||||||
|
- Supported `Value` write targets:
|
||||||
- `TEXT_AREA`, single-select `DROPDOWN`, `WEB_CONTENT_READER`, `FILE_CONTENT_READER`, `COLOR_PICKER`, `DATE_PICKER`, `DATE_RANGE_PICKER`, `TIME_PICKER`: string values
|
- `TEXT_AREA`, single-select `DROPDOWN`, `WEB_CONTENT_READER`, `FILE_CONTENT_READER`, `COLOR_PICKER`, `DATE_PICKER`, `DATE_RANGE_PICKER`, `TIME_PICKER`: string values
|
||||||
- multiselect `DROPDOWN`: array-like Lua table of strings
|
- multiselect `DROPDOWN`: array-like Lua table of strings
|
||||||
- `SWITCH`: boolean values
|
- `SWITCH`: boolean values
|
||||||
- Unknown field names and wrong value types are ignored and logged.
|
- Unknown component names, wrong value types, unsupported prop values, and non-writeable props are ignored and logged.
|
||||||
|
|
||||||
#### Example Button component
|
#### Example Button component
|
||||||
```lua
|
```lua
|
||||||
@ -296,8 +303,8 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
["IconColor"] = "Inherit",
|
["IconColor"] = "Inherit",
|
||||||
["IconSize"] = "Medium",
|
["IconSize"] = "Medium",
|
||||||
["Action"] = function(input)
|
["Action"] = function(input)
|
||||||
local email = input.fields.emailContent or ""
|
local email = input.emailContent and input.emailContent.Value or ""
|
||||||
local translate = input.fields.translateEmail or false
|
local translate = input.translateEmail and input.translateEmail.Value or false
|
||||||
local output = email
|
local output = email
|
||||||
|
|
||||||
if translate then
|
if translate then
|
||||||
@ -305,8 +312,10 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fields = {
|
state = {
|
||||||
outputTextField = output
|
outputTextField = {
|
||||||
|
Value = output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
@ -330,8 +339,10 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
["StartIcon"] = "Icons.Material.Filled.Refresh",
|
["StartIcon"] = "Icons.Material.Filled.Refresh",
|
||||||
["Action"] = function(input)
|
["Action"] = function(input)
|
||||||
return {
|
return {
|
||||||
fields = {
|
state = {
|
||||||
outputTextField = "Preview refreshed at " .. Timestamp()
|
outputTextField = {
|
||||||
|
Value = "Preview refreshed at " .. Timestamp()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -374,8 +385,10 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
["Text"] = "Build output",
|
["Text"] = "Build output",
|
||||||
["Action"] = function(input)
|
["Action"] = function(input)
|
||||||
return {
|
return {
|
||||||
fields = {
|
state = {
|
||||||
outputBuffer = input.fields.emailContent or ""
|
outputBuffer = {
|
||||||
|
Value = input.emailContent and input.emailContent.Value or ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
@ -388,7 +401,8 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
["Name"] = "logColor",
|
["Name"] = "logColor",
|
||||||
["Text"] = "Log color",
|
["Text"] = "Log color",
|
||||||
["Action"] = function(input)
|
["Action"] = function(input)
|
||||||
LogError("ColorPicker value: " .. tostring(input.fields.colorPicker or ""))
|
local colorValue = input.colorPicker and input.colorPicker.Value or ""
|
||||||
|
LogError("ColorPicker value: " .. colorValue)
|
||||||
return nil
|
return nil
|
||||||
end,
|
end,
|
||||||
["EndIcon"] = "Icons.Material.Filled.BugReport"
|
["EndIcon"] = "Icons.Material.Filled.BugReport"
|
||||||
@ -402,11 +416,11 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
### `SWITCH` reference
|
### `SWITCH` reference
|
||||||
- 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)`.
|
||||||
- `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.
|
- `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 `{ state = { ... } }` to update component state. The new switch value is already reflected in `input.<Name>.Value`.
|
||||||
- `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.
|
||||||
- `LabelOn`: text shown when the switch value is `true`.
|
- `LabelOn`: text shown when the switch value is `true`.
|
||||||
@ -427,10 +441,12 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
["Label"] = "Include summary",
|
["Label"] = "Include summary",
|
||||||
["Value"] = true,
|
["Value"] = true,
|
||||||
["OnChanged"] = function(input)
|
["OnChanged"] = function(input)
|
||||||
local includeSummary = input.fields.IncludeSummary or false
|
local includeSummary = input.IncludeSummary and input.IncludeSummary.Value or false
|
||||||
return {
|
return {
|
||||||
fields = {
|
state = {
|
||||||
SummaryMode = includeSummary and "short-summary" or "no-summary"
|
SummaryMode = {
|
||||||
|
Value = includeSummary and "short-summary" or "no-summary"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
@ -452,7 +468,7 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
### `COLOR_PICKER` reference
|
### `COLOR_PICKER` reference
|
||||||
- Use `Type = "COLOR_PICKER"` to render a MudBlazor color picker.
|
- Use `Type = "COLOR_PICKER"` to render a MudBlazor color picker.
|
||||||
- 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)`.
|
||||||
- `Label`: visible field label.
|
- `Label`: visible field label.
|
||||||
- Optional props:
|
- Optional props:
|
||||||
- `Placeholder`: default color hex string (e.g. `#FF10FF`) or initial hint text.
|
- `Placeholder`: default color hex string (e.g. `#FF10FF`) or initial hint text.
|
||||||
@ -485,7 +501,7 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
### `DATE_PICKER` reference
|
### `DATE_PICKER` reference
|
||||||
- Use `Type = "DATE_PICKER"` to render a MudBlazor date picker.
|
- Use `Type = "DATE_PICKER"` to render a MudBlazor date picker.
|
||||||
- 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)`.
|
||||||
- `Label`: visible field label.
|
- `Label`: visible field label.
|
||||||
- Optional props:
|
- Optional props:
|
||||||
- `Value`: initial date string. Use the same format as `DateFormat`; default recommendation is `yyyy-MM-dd`.
|
- `Value`: initial date string. Use the same format as `DateFormat`; default recommendation is `yyyy-MM-dd`.
|
||||||
@ -520,7 +536,7 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
### `DATE_RANGE_PICKER` reference
|
### `DATE_RANGE_PICKER` reference
|
||||||
- Use `Type = "DATE_RANGE_PICKER"` to render a MudBlazor date range picker.
|
- Use `Type = "DATE_RANGE_PICKER"` to render a MudBlazor date range picker.
|
||||||
- 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)`.
|
||||||
- `Label`: visible field label.
|
- `Label`: visible field label.
|
||||||
- Optional props:
|
- Optional props:
|
||||||
- `Value`: initial range string using `<start> - <end>`, for example `2026-03-01 - 2026-03-31`.
|
- `Value`: initial range string using `<start> - <end>`, for example `2026-03-01 - 2026-03-31`.
|
||||||
@ -557,7 +573,7 @@ More information on rendered components can be found [here](https://www.mudblazo
|
|||||||
### `TIME_PICKER` reference
|
### `TIME_PICKER` reference
|
||||||
- Use `Type = "TIME_PICKER"` to render a MudBlazor time picker.
|
- Use `Type = "TIME_PICKER"` to render a MudBlazor time picker.
|
||||||
- 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)`.
|
||||||
- `Label`: visible field label.
|
- `Label`: visible field label.
|
||||||
- Optional props:
|
- Optional props:
|
||||||
- `Value`: initial time string. Use the same format as `TimeFormat`; default recommendations are `HH:mm` or `hh:mm tt`.
|
- `Value`: initial time string. Use the same format as `TimeFormat`; default recommendations are `HH:mm` or `hh:mm tt`.
|
||||||
@ -613,34 +629,24 @@ If you want full control over prompt composition, define `ASSISTANT.BuildPrompt`
|
|||||||
---
|
---
|
||||||
### `input` table shape
|
### `input` table shape
|
||||||
The function receives a single `input` Lua table with:
|
The function receives a single `input` Lua table with:
|
||||||
- `input.fields`: values keyed by component `Name`
|
- `input.<Name>`: one entry per named component
|
||||||
- Text area, single-select dropdown, and readers are strings
|
|
||||||
- Multiselect dropdown is an array-like Lua table of strings
|
|
||||||
- Switch is a boolean
|
|
||||||
- Color picker is the selected color as a string
|
|
||||||
- Date picker is the selected date as a string
|
|
||||||
- Date range picker is the selected range as a single string in `<start> - <end>` format
|
|
||||||
- Time picker is the selected time as a string
|
|
||||||
- `input.meta`: per-component metadata keyed by component `Name`
|
|
||||||
- `Type` (string, e.g. `TEXT_AREA`, `DROPDOWN`, `SWITCH`, `COLOR_PICKER`, `DATE_PICKER`, `DATE_RANGE_PICKER`, `TIME_PICKER`)
|
- `Type` (string, e.g. `TEXT_AREA`, `DROPDOWN`, `SWITCH`, `COLOR_PICKER`, `DATE_PICKER`, `DATE_RANGE_PICKER`, `TIME_PICKER`)
|
||||||
- `Label` (string, when provided)
|
- `Value` (current component value)
|
||||||
- `UserPrompt` (string, when provided)
|
- `Props` (readable component props)
|
||||||
- `input.profile`: selected profile data
|
- `input.profile`: selected profile data
|
||||||
- `Name`, `NeedToKnow`, `Actions`, `Num`
|
- `Name`, `NeedToKnow`, `Actions`, `Num`
|
||||||
- When no profile is selected, values match the built-in "Use no profile" entry
|
- When no profile is selected, values match the built-in "Use no profile" entry
|
||||||
|
- `profile` is a reserved key in the input table
|
||||||
```
|
```
|
||||||
input = {
|
input = {
|
||||||
fields = {
|
["<Name>"] = {
|
||||||
["<Name>"] = "<string|boolean>",
|
Type = "<TEXT_AREA|DROPDOWN|SWITCH|WEB_CONTENT_READER|FILE_CONTENT_READER|COLOR_PICKER|DATE_PICKER|DATE_RANGE_PICKER|TIME_PICKER>",
|
||||||
...
|
Value = "<string|boolean|table>",
|
||||||
},
|
Props = {
|
||||||
meta = {
|
Name = "<string>",
|
||||||
["<Name>"] = {
|
|
||||||
Type = "<TEXT_AREA|DROPDOWN|SWITCH|WEB_CONTENT_READER|FILE_CONTENT_READER|COLOR_PICKER|DATE_PICKER|DATE_RANGE_PICKER|TIME_PICKER>",
|
|
||||||
Label = "<string?>",
|
Label = "<string?>",
|
||||||
UserPrompt = "<string?>"
|
UserPrompt = "<string?>"
|
||||||
},
|
}
|
||||||
...
|
|
||||||
},
|
},
|
||||||
profile = {
|
profile = {
|
||||||
Name = "<string>",
|
Name = "<string>",
|
||||||
@ -654,47 +660,66 @@ input = {
|
|||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
### Using `meta` inside BuildPrompt
|
### Using component metadata inside BuildPrompt
|
||||||
`input.meta` is useful when you want to dynamically build the prompt based on component type or reuse existing UI text (labels/user prompts).
|
`input.<Name>.Type` and `input.<Name>.Props` are useful when you want to build prompts from a few specific fields without depending on the default `UserPrompt` assembly.
|
||||||
|
|
||||||
#### Example: iterate all fields with labels and include their values
|
#### Example: build a prompt from two fields
|
||||||
```lua
|
```lua
|
||||||
ASSISTANT.BuildPrompt = function(input)
|
ASSISTANT.BuildPrompt = function(input)
|
||||||
|
local topic = input.Topic and input.Topic.Value or ""
|
||||||
|
local includeSummary = input.IncludeSummary and input.IncludeSummary.Value or false
|
||||||
|
|
||||||
local parts = {}
|
local parts = {}
|
||||||
for name, value in pairs(input.fields) do
|
if topic ~= "" then
|
||||||
local meta = input.meta[name]
|
table.insert(parts, "Topic: " .. topic)
|
||||||
if meta and meta.Label and value ~= "" then
|
|
||||||
table.insert(parts, meta.Label .. ": " .. tostring(value))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if includeSummary then
|
||||||
|
table.insert(parts, "Add a short summary at the end.")
|
||||||
|
end
|
||||||
|
|
||||||
return table.concat(parts, "\n")
|
return table.concat(parts, "\n")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Example: handle types differently
|
#### Example: reuse a label from `Props`
|
||||||
```lua
|
```lua
|
||||||
ASSISTANT.BuildPrompt = function(input)
|
ASSISTANT.BuildPrompt = function(input)
|
||||||
local parts = {}
|
local main = input.Main
|
||||||
for name, meta in pairs(input.meta) do
|
if not main then
|
||||||
local value = input.fields[name]
|
return ""
|
||||||
if meta.Type == "SWITCH" then
|
|
||||||
table.insert(parts, name .. ": " .. tostring(value))
|
|
||||||
elseif meta.Type == "COLOR_PICKER" and value and value ~= "" then
|
|
||||||
table.insert(parts, name .. ": " .. value)
|
|
||||||
elseif meta.Type == "DATE_PICKER" and value and value ~= "" then
|
|
||||||
table.insert(parts, name .. ": " .. value)
|
|
||||||
elseif meta.Type == "DATE_RANGE_PICKER" and value and value ~= "" then
|
|
||||||
table.insert(parts, name .. ": " .. value)
|
|
||||||
elseif meta.Type == "TIME_PICKER" and value and value ~= "" then
|
|
||||||
table.insert(parts, name .. ": " .. value)
|
|
||||||
elseif value and value ~= "" then
|
|
||||||
table.insert(parts, name .. ": " .. value)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return table.concat(parts, "\n")
|
|
||||||
|
local label = main.Props and main.Props.Label or "Main"
|
||||||
|
local value = main.Value or ""
|
||||||
|
return label .. ": " .. value
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Callback result shape
|
||||||
|
Callbacks may return a partial state update:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
return {
|
||||||
|
state = {
|
||||||
|
["<Name>"] = {
|
||||||
|
Value = "<optional new value>",
|
||||||
|
Props = {
|
||||||
|
-- optional writable prop updates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Value` is optional
|
||||||
|
- `Props` is optional
|
||||||
|
- `Props` updates are partial
|
||||||
|
- non-writeable props are ignored and logged
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Using `profile` inside BuildPrompt
|
### Using `profile` inside BuildPrompt
|
||||||
Profiles are optional user context (e.g., "NeedToKnow" and "Actions"). You can inject this directly into the user prompt if you want the LLM to always see it.
|
Profiles are optional user context (e.g., "NeedToKnow" and "Actions"). You can inject this directly into the user prompt if you want the LLM to always see it.
|
||||||
|
|
||||||
@ -707,7 +732,7 @@ ASSISTANT.BuildPrompt = function(input)
|
|||||||
table.insert(parts, input.profile.NeedToKnow)
|
table.insert(parts, input.profile.NeedToKnow)
|
||||||
table.insert(parts, "")
|
table.insert(parts, "")
|
||||||
end
|
end
|
||||||
table.insert(parts, input.fields.Main or "")
|
table.insert(parts, input.Main and input.Main.Value or "")
|
||||||
return table.concat(parts, "\n")
|
return table.concat(parts, "\n")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@ -1012,14 +1037,14 @@ The assistant runtime exposes basic logging helpers to Lua. Use them to debug cu
|
|||||||
|
|
||||||
- `LogDebug(message)`
|
- `LogDebug(message)`
|
||||||
- `LogInfo(message)`
|
- `LogInfo(message)`
|
||||||
- `LogWarn(message)`
|
- `LogWarning(message)`
|
||||||
- `LogError(message)`
|
- `LogError(message)`
|
||||||
|
|
||||||
#### Example: Use Logging in lua functions
|
#### Example: Use Logging in lua functions
|
||||||
```lua
|
```lua
|
||||||
ASSISTANT.BuildPrompt = function(input)
|
ASSISTANT.BuildPrompt = function(input)
|
||||||
LogInfo("BuildPrompt called")
|
LogInfo("BuildPrompt called")
|
||||||
return input.fields.Text or ""
|
return input.Text and input.Text.Value or ""
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|||||||
@ -147,8 +147,8 @@ ASSISTANT = {
|
|||||||
["IconColor"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>", -- color of start and end icons on text buttons. Defaults to Inherit
|
["IconColor"] = "<Dark|Error|Info|Inherit|Primary|Secondary|Success|Surface|Tertiary|Transparent|Warning>", -- color of start and end icons on text buttons. Defaults to Inherit
|
||||||
["IconSize"] = "<Small|Medium|Large>", -- size of icons. Defaults to null. When null, the value of ["Size"] is used
|
["IconSize"] = "<Small|Medium|Large>", -- size of icons. Defaults to null. When null, the value of ["Size"] is used
|
||||||
["Action"] = function(input)
|
["Action"] = function(input)
|
||||||
local email = input.fields.emailContent or ""
|
local email = input.emailContent and input.emailContent.Value or ""
|
||||||
local translate = input.fields.translateEmail or false
|
local translate = input.translateEmail and input.translateEmail.Value or false
|
||||||
local output = email
|
local output = email
|
||||||
|
|
||||||
if translate then
|
if translate then
|
||||||
@ -156,8 +156,10 @@ ASSISTANT = {
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fields = {
|
state = {
|
||||||
outputBuffer = output
|
outputBuffer = {
|
||||||
|
Value = output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using System.Collections;
|
|
||||||
using AIStudio.Assistants.Dynamic;
|
using AIStudio.Assistants.Dynamic;
|
||||||
using Lua;
|
using Lua;
|
||||||
|
|
||||||
@ -159,6 +158,7 @@ public sealed class AssistantState
|
|||||||
{
|
{
|
||||||
target[named.Name] = new LuaTable
|
target[named.Name] = new LuaTable
|
||||||
{
|
{
|
||||||
|
["Type"] = Enum.GetName<AssistantComponentType>(component.Type) ?? string.Empty,
|
||||||
["Value"] = component is IStatefulAssistantComponent ? this.ReadValueForLua(named.Name) : LuaValue.Nil,
|
["Value"] = component is IStatefulAssistantComponent ? this.ReadValueForLua(named.Name) : LuaValue.Nil,
|
||||||
["Props"] = this.CreatePropsTable(component),
|
["Props"] = this.CreatePropsTable(component),
|
||||||
};
|
};
|
||||||
@ -176,7 +176,7 @@ public sealed class AssistantState
|
|||||||
if (this.SingleSelect.TryGetValue(name, out var singleSelectValue))
|
if (this.SingleSelect.TryGetValue(name, out var singleSelectValue))
|
||||||
return singleSelectValue;
|
return singleSelectValue;
|
||||||
if (this.MultiSelect.TryGetValue(name, out var multiSelectValue))
|
if (this.MultiSelect.TryGetValue(name, out var multiSelectValue))
|
||||||
return CreateLuaArray(multiSelectValue.OrderBy(static value => value, StringComparer.Ordinal));
|
return AssistantLuaConversion.CreateLuaArray(multiSelectValue.OrderBy(static value => value, StringComparer.Ordinal));
|
||||||
if (this.Bools.TryGetValue(name, out var boolValue))
|
if (this.Bools.TryGetValue(name, out var boolValue))
|
||||||
return boolValue;
|
return boolValue;
|
||||||
if (this.WebContent.TryGetValue(name, out var webContentValue))
|
if (this.WebContent.TryGetValue(name, out var webContentValue))
|
||||||
@ -210,138 +210,13 @@ public sealed class AssistantState
|
|||||||
if (!component.Props.TryGetValue(key, out var value))
|
if (!component.Props.TryGetValue(key, out var value))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!TryWriteLuaValue(table, key, value))
|
if (!AssistantLuaConversion.TryWriteAssistantValue(table, key, value))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryWriteLuaValue(LuaTable table, string key, object? value)
|
|
||||||
{
|
|
||||||
if (value is null or LuaFunction)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (value)
|
|
||||||
{
|
|
||||||
case LuaValue { Type: not LuaValueType.Nil } luaValue:
|
|
||||||
table[key] = luaValue;
|
|
||||||
return true;
|
|
||||||
case LuaTable luaTable:
|
|
||||||
table[key] = luaTable;
|
|
||||||
return true;
|
|
||||||
case string stringValue:
|
|
||||||
table[key] = (LuaValue)stringValue;
|
|
||||||
return true;
|
|
||||||
case bool boolValue:
|
|
||||||
table[key] = (LuaValue)boolValue;
|
|
||||||
return true;
|
|
||||||
case byte byteValue:
|
|
||||||
table[key] = (LuaValue)byteValue;
|
|
||||||
return true;
|
|
||||||
case sbyte sbyteValue:
|
|
||||||
table[key] = (LuaValue)sbyteValue;
|
|
||||||
return true;
|
|
||||||
case short shortValue:
|
|
||||||
table[key] = (LuaValue)shortValue;
|
|
||||||
return true;
|
|
||||||
case ushort ushortValue:
|
|
||||||
table[key] = (LuaValue)ushortValue;
|
|
||||||
return true;
|
|
||||||
case int intValue:
|
|
||||||
table[key] = (LuaValue)intValue;
|
|
||||||
return true;
|
|
||||||
case uint uintValue:
|
|
||||||
table[key] = (LuaValue)uintValue;
|
|
||||||
return true;
|
|
||||||
case long longValue:
|
|
||||||
table[key] = (LuaValue)longValue;
|
|
||||||
return true;
|
|
||||||
case ulong ulongValue:
|
|
||||||
table[key] = (LuaValue)ulongValue;
|
|
||||||
return true;
|
|
||||||
case float floatValue:
|
|
||||||
table[key] = (LuaValue)floatValue;
|
|
||||||
return true;
|
|
||||||
case double doubleValue:
|
|
||||||
table[key] = (LuaValue)doubleValue;
|
|
||||||
return true;
|
|
||||||
case decimal decimalValue:
|
|
||||||
table[key] = (LuaValue)(double)decimalValue;
|
|
||||||
return true;
|
|
||||||
case Enum enumValue:
|
|
||||||
table[key] = enumValue.ToString() ?? string.Empty;
|
|
||||||
return true;
|
|
||||||
case AssistantDropdownItem dropdownItem:
|
|
||||||
table[key] = CreateDropdownItemTable(dropdownItem);
|
|
||||||
return true;
|
|
||||||
case IEnumerable<AssistantDropdownItem> dropdownItems:
|
|
||||||
table[key] = CreateLuaArray(dropdownItems.Select(CreateDropdownItemTable));
|
|
||||||
return true;
|
|
||||||
case IEnumerable<AssistantListItem> listItems:
|
|
||||||
table[key] = CreateLuaArray(listItems.Select(CreateListItemTable));
|
|
||||||
return true;
|
|
||||||
case IEnumerable<string> strings:
|
|
||||||
table[key] = CreateLuaArray(strings);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LuaTable CreateDropdownItemTable(AssistantDropdownItem item) =>
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
["Value"] = item.Value,
|
|
||||||
["Display"] = item.Display,
|
|
||||||
};
|
|
||||||
|
|
||||||
private static LuaTable CreateListItemTable(AssistantListItem item)
|
|
||||||
{
|
|
||||||
var table = new LuaTable
|
|
||||||
{
|
|
||||||
["Type"] = item.Type,
|
|
||||||
["Text"] = item.Text,
|
|
||||||
["Icon"] = item.Icon,
|
|
||||||
["IconColor"] = item.IconColor,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(item.Href))
|
|
||||||
table["Href"] = item.Href;
|
|
||||||
|
|
||||||
return table;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LuaTable CreateLuaArray(IEnumerable values)
|
|
||||||
{
|
|
||||||
var luaArray = new LuaTable();
|
|
||||||
var index = 1;
|
|
||||||
|
|
||||||
foreach (var value in values)
|
|
||||||
luaArray[index++] = value switch
|
|
||||||
{
|
|
||||||
null => LuaValue.Nil,
|
|
||||||
LuaValue luaValue => luaValue,
|
|
||||||
LuaTable luaTable => luaTable,
|
|
||||||
string stringValue => (LuaValue)stringValue,
|
|
||||||
bool boolValue => (LuaValue)boolValue,
|
|
||||||
byte byteValue => (LuaValue)byteValue,
|
|
||||||
sbyte sbyteValue => (LuaValue)sbyteValue,
|
|
||||||
short shortValue => (LuaValue)shortValue,
|
|
||||||
ushort ushortValue => (LuaValue)ushortValue,
|
|
||||||
int intValue => (LuaValue)intValue,
|
|
||||||
uint uintValue => (LuaValue)uintValue,
|
|
||||||
long longValue => (LuaValue)longValue,
|
|
||||||
ulong ulongValue => (LuaValue)ulongValue,
|
|
||||||
float floatValue => (LuaValue)floatValue,
|
|
||||||
double doubleValue => (LuaValue)doubleValue,
|
|
||||||
decimal decimalValue => (LuaValue)(double)decimalValue,
|
|
||||||
_ => LuaValue.Nil,
|
|
||||||
};
|
|
||||||
|
|
||||||
return luaArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static HashSet<string> ReadStringValues(LuaTable values)
|
private static HashSet<string> ReadStringValues(LuaTable values)
|
||||||
{
|
{
|
||||||
var parsedValues = new HashSet<string>(StringComparer.Ordinal);
|
var parsedValues = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
|||||||
@ -399,141 +399,7 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.TryConvertLuaValue(val, out result);
|
return AssistantLuaConversion.TryReadScalarOrStructuredValue(val, out result);
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryConvertLuaValue(LuaValue val, out object result)
|
|
||||||
{
|
|
||||||
if (val.TryRead<string>(out var s))
|
|
||||||
{
|
|
||||||
result = s;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (val.TryRead<bool>(out var b))
|
|
||||||
{
|
|
||||||
result = b;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (val.TryRead<double>(out var d))
|
|
||||||
{
|
|
||||||
result = d;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.TryRead<LuaTable>(out var table) && this.TryParseDropdownItem(table, out var item))
|
|
||||||
{
|
|
||||||
result = item;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.TryRead<LuaTable>(out var listTable) && this.TryParseDropdownItemList(listTable, out var itemList))
|
|
||||||
{
|
|
||||||
result = itemList;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val.TryRead<LuaTable>(out var listItemListTable) && this.TryParseListItemList(listItemListTable, out var listItemList))
|
|
||||||
{
|
|
||||||
result = listItemList;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = null!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryParseDropdownItem(LuaTable table, out AssistantDropdownItem item)
|
|
||||||
{
|
|
||||||
item = new AssistantDropdownItem();
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Value", out var valueVal) || !valueVal.TryRead<string>(out var value))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Display", out var displayVal) || !displayVal.TryRead<string>(out var display))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
item.Value = value;
|
|
||||||
item.Display = display;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private bool TryParseDropdownItemList(LuaTable table, out List<AssistantDropdownItem> items)
|
|
||||||
{
|
|
||||||
items = new List<AssistantDropdownItem>();
|
|
||||||
|
|
||||||
var length = table.ArrayLength;
|
|
||||||
for (var i = 1; i <= length; i++)
|
|
||||||
{
|
|
||||||
var value = table[i];
|
|
||||||
|
|
||||||
if (value.TryRead<LuaTable>(out var subTable) && this.TryParseDropdownItem(subTable, out var item))
|
|
||||||
{
|
|
||||||
items.Add(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
items = null!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryParseListItem(LuaTable table, out AssistantListItem item)
|
|
||||||
{
|
|
||||||
item = new AssistantListItem();
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Text", out var textVal) || !textVal.TryRead<string>(out var text))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!table.TryGetValue("Type", out var typeVal) || !typeVal.TryRead<string>(out var type))
|
|
||||||
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.Type = type;
|
|
||||||
item.Icon = icon;
|
|
||||||
item.IconColor = iconColor;
|
|
||||||
|
|
||||||
if (table.TryGetValue("Href", out var hrefVal) && hrefVal.TryRead<string>(out var href))
|
|
||||||
{
|
|
||||||
item.Href = href;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryParseListItemList(LuaTable table, out List<AssistantListItem> items)
|
|
||||||
{
|
|
||||||
items = new List<AssistantListItem>();
|
|
||||||
|
|
||||||
var length = table.ArrayLength;
|
|
||||||
for (var i = 1; i <= length; i++)
|
|
||||||
{
|
|
||||||
var value = table[i];
|
|
||||||
|
|
||||||
if (value.TryRead<LuaTable>(out var subTable) && this.TryParseListItem(subTable, out var item))
|
|
||||||
{
|
|
||||||
items.Add(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
items = null!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterLuaHelpers()
|
private void RegisterLuaHelpers()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user