diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
index cacd77d2..dc1f27e8 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
+++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor
@@ -403,6 +403,70 @@ else
}
break;
+ case AssistantComponentType.DATE_PICKER:
+ if (component is AssistantDatePicker assistantDatePicker)
+ {
+ var datePicker = assistantDatePicker;
+ var format = datePicker.GetDateFormat();
+
+
+
+
+ }
+ break;
+ case AssistantComponentType.DATE_RANGE_PICKER:
+ if (component is AssistantDateRangePicker assistantDateRangePicker)
+ {
+ var dateRangePicker = assistantDateRangePicker;
+ var format = dateRangePicker.GetDateFormat();
+
+
+
+
+ }
+ break;
+ case AssistantComponentType.TIME_PICKER:
+ if (component is AssistantTimePicker assistantTimePicker)
+ {
+ var timePicker = assistantTimePicker;
+ var format = timePicker.GetTimeFormat();
+
+
+
+
+ }
+ break;
}
;
diff --git a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs
index fa193f4f..d576df94 100644
--- a/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs
+++ b/app/MindWork AI Studio/Assistants/Dynamic/AssistantDynamic.razor.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
@@ -45,12 +46,18 @@ public partial class AssistantDynamic : AssistantBaseCore
private readonly Dictionary webContentFields = new();
private readonly Dictionary fileContentFields = new();
private readonly Dictionary colorPickerFields = new();
+ private readonly Dictionary datePickerFields = new();
+ private readonly Dictionary dateRangePickerFields = new();
+ private readonly Dictionary timePickerFields = new();
private readonly Dictionary imageCache = new();
private readonly HashSet executingButtonActions = [];
private readonly HashSet executingSwitchActions = [];
private string pluginPath = string.Empty;
private const string PLUGIN_SCHEME = "plugin://";
private const string ASSISTANT_QUERY_KEY = "assistantId";
+ private static readonly CultureInfo INVARIANT_CULTURE = CultureInfo.InvariantCulture;
+ private static readonly string[] FALLBACK_DATE_FORMATS = ["yyyy-MM-dd", "dd.MM.yyyy", "MM/dd/yyyy"];
+ private static readonly string[] FALLBACK_TIME_FORMATS = ["HH:mm", "HH:mm:ss", "hh:mm tt", "h:mm tt"];
protected override void OnInitialized()
{
@@ -136,6 +143,18 @@ public partial class AssistantDynamic : AssistantBaseCore
{
this.colorPickerFields[entry.Key] = string.Empty;
}
+ foreach (var entry in this.datePickerFields)
+ {
+ this.datePickerFields[entry.Key] = string.Empty;
+ }
+ foreach (var entry in this.dateRangePickerFields)
+ {
+ this.dateRangePickerFields[entry.Key] = string.Empty;
+ }
+ foreach (var entry in this.timePickerFields)
+ {
+ this.timePickerFields[entry.Key] = string.Empty;
+ }
}
protected override bool MightPreselectValues()
@@ -229,6 +248,12 @@ public partial class AssistantDynamic : AssistantBaseCore
fields[entry.Key] = entry.Value.Content ?? string.Empty;
foreach (var entry in this.colorPickerFields)
fields[entry.Key] = entry.Value ?? string.Empty;
+ foreach (var entry in this.datePickerFields)
+ fields[entry.Key] = entry.Value ?? string.Empty;
+ foreach (var entry in this.dateRangePickerFields)
+ fields[entry.Key] = entry.Value ?? string.Empty;
+ foreach (var entry in this.timePickerFields)
+ fields[entry.Key] = entry.Value ?? string.Empty;
input["fields"] = fields;
@@ -325,6 +350,18 @@ public partial class AssistantDynamic : AssistantBaseCore
if (component is AssistantColorPicker assistantColorPicker && !this.colorPickerFields.ContainsKey(assistantColorPicker.Name))
this.colorPickerFields.Add(assistantColorPicker.Name, assistantColorPicker.Placeholder);
break;
+ case AssistantComponentType.DATE_PICKER:
+ if (component is AssistantDatePicker datePicker && !this.datePickerFields.ContainsKey(datePicker.Name))
+ this.datePickerFields.Add(datePicker.Name, datePicker.Value);
+ break;
+ case AssistantComponentType.DATE_RANGE_PICKER:
+ if (component is AssistantDateRangePicker dateRangePicker && !this.dateRangePickerFields.ContainsKey(dateRangePicker.Name))
+ this.dateRangePickerFields.Add(dateRangePicker.Name, dateRangePicker.Value);
+ break;
+ case AssistantComponentType.TIME_PICKER:
+ if (component is AssistantTimePicker timePicker && !this.timePickerFields.ContainsKey(timePicker.Name))
+ this.timePickerFields.Add(timePicker.Name, timePicker.Value);
+ break;
}
if (component.Children.Count > 0)
@@ -473,6 +510,33 @@ public partial class AssistantDynamic : AssistantBaseCore
return;
}
+ if (this.datePickerFields.ContainsKey(fieldName))
+ {
+ if (value.TryRead(out var dateValue))
+ this.datePickerFields[fieldName] = dateValue ?? string.Empty;
+ else
+ this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
+ return;
+ }
+
+ if (this.dateRangePickerFields.ContainsKey(fieldName))
+ {
+ if (value.TryRead(out var dateRangeValue))
+ this.dateRangePickerFields[fieldName] = dateRangeValue ?? string.Empty;
+ else
+ this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
+ return;
+ }
+
+ if (this.timePickerFields.ContainsKey(fieldName))
+ {
+ if (value.TryRead(out var timeValue))
+ this.timePickerFields[fieldName] = timeValue ?? string.Empty;
+ else
+ this.LogFieldUpdateTypeMismatch(fieldName, "string", sourceType);
+ return;
+ }
+
if (this.webContentFields.TryGetValue(fieldName, out var webContentState))
{
if (value.TryRead(out var webContentValue))
@@ -549,6 +613,15 @@ public partial class AssistantDynamic : AssistantBaseCore
case AssistantColorPicker colorPicker:
this.AddMetaEntry(meta, colorPicker.Name, component.Type, colorPicker.Label, colorPicker.UserPrompt);
break;
+ case AssistantDatePicker datePicker:
+ this.AddMetaEntry(meta, datePicker.Name, component.Type, datePicker.Label, datePicker.UserPrompt);
+ break;
+ case AssistantDateRangePicker dateRangePicker:
+ this.AddMetaEntry(meta, dateRangePicker.Name, component.Type, dateRangePicker.Label, dateRangePicker.UserPrompt);
+ break;
+ case AssistantTimePicker timePicker:
+ this.AddMetaEntry(meta, timePicker.Name, component.Type, timePicker.Label, timePicker.UserPrompt);
+ break;
}
if (component.Children.Count > 0)
@@ -623,6 +696,30 @@ public partial class AssistantDynamic : AssistantBaseCore
prompt += $"user prompt:{Environment.NewLine}{userInput}";
}
break;
+ case AssistantComponentType.DATE_PICKER:
+ if (component is AssistantDatePicker datePicker)
+ {
+ prompt += $"context:{Environment.NewLine}{datePicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
+ if (this.datePickerFields.TryGetValue(datePicker.Name, out userInput))
+ prompt += $"user prompt:{Environment.NewLine}{userInput}";
+ }
+ break;
+ case AssistantComponentType.DATE_RANGE_PICKER:
+ if (component is AssistantDateRangePicker dateRangePicker)
+ {
+ prompt += $"context:{Environment.NewLine}{dateRangePicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
+ if (this.dateRangePickerFields.TryGetValue(dateRangePicker.Name, out userInput))
+ prompt += $"user prompt:{Environment.NewLine}{userInput}";
+ }
+ break;
+ case AssistantComponentType.TIME_PICKER:
+ if (component is AssistantTimePicker timePicker)
+ {
+ prompt += $"context:{Environment.NewLine}{timePicker.UserPrompt}{Environment.NewLine}---{Environment.NewLine}";
+ if (this.timePickerFields.TryGetValue(timePicker.Name, out userInput))
+ prompt += $"user prompt:{Environment.NewLine}{userInput}";
+ }
+ break;
}
if (component.Children.Count > 0)
@@ -663,4 +760,124 @@ public partial class AssistantDynamic : AssistantBaseCore
return parsedValues;
}
+
+ private DateTime? ParseDatePickerValue(string? value, string? format)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ return null;
+
+ if (TryParseDate(value, format, out var parsedDate))
+ return parsedDate;
+
+ return null;
+ }
+
+ private void SetDatePickerValue(string fieldName, DateTime? value, string? format)
+ {
+ this.datePickerFields[fieldName] = value.HasValue ? FormatDate(value.Value, format) : string.Empty;
+ }
+
+ private DateRange? ParseDateRangePickerValue(string? value, string? format)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ return null;
+
+ var parts = value.Split(" - ", 2, StringSplitOptions.TrimEntries);
+ if (parts.Length != 2)
+ return null;
+
+ if (!TryParseDate(parts[0], format, out var start) || !TryParseDate(parts[1], format, out var end))
+ return null;
+
+ return new DateRange(start, end);
+ }
+
+ private void SetDateRangePickerValue(string fieldName, DateRange? value, string? format)
+ {
+ if (value?.Start is null || value.End is null)
+ {
+ this.dateRangePickerFields[fieldName] = string.Empty;
+ return;
+ }
+
+ this.dateRangePickerFields[fieldName] = $"{FormatDate(value.Start.Value, format)} - {FormatDate(value.End.Value, format)}";
+ }
+
+ private TimeSpan? ParseTimePickerValue(string? value, string? format)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ return null;
+
+ if (TryParseTime(value, format, out var parsedTime))
+ return parsedTime;
+
+ return null;
+ }
+
+ private void SetTimePickerValue(string fieldName, TimeSpan? value, string? format)
+ {
+ this.timePickerFields[fieldName] = value.HasValue ? FormatTime(value.Value, format) : string.Empty;
+ }
+
+ private static bool TryParseDate(string value, string? format, out DateTime parsedDate)
+ {
+ if (!string.IsNullOrWhiteSpace(format) &&
+ DateTime.TryParseExact(value, format, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate))
+ {
+ return true;
+ }
+
+ if (DateTime.TryParseExact(value, FALLBACK_DATE_FORMATS, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate))
+ return true;
+
+ return DateTime.TryParse(value, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out parsedDate);
+ }
+
+ private static bool TryParseTime(string value, string? format, out TimeSpan parsedTime)
+ {
+ if (!string.IsNullOrWhiteSpace(format) &&
+ DateTime.TryParseExact(value, format, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out var dateTime))
+ {
+ parsedTime = dateTime.TimeOfDay;
+ return true;
+ }
+
+ if (DateTime.TryParseExact(value, FALLBACK_TIME_FORMATS, INVARIANT_CULTURE, DateTimeStyles.AllowWhiteSpaces, out dateTime))
+ {
+ parsedTime = dateTime.TimeOfDay;
+ return true;
+ }
+
+ if (TimeSpan.TryParse(value, INVARIANT_CULTURE, out parsedTime))
+ return true;
+
+ parsedTime = default;
+ return false;
+ }
+
+ private static string FormatDate(DateTime value, string? format)
+ {
+ try
+ {
+ return value.ToString(string.IsNullOrWhiteSpace(format) ? "yyyy-MM-dd" : format, INVARIANT_CULTURE);
+ }
+ catch (FormatException)
+ {
+ return value.ToString("yyyy-MM-dd", INVARIANT_CULTURE);
+ }
+ }
+
+ private static string FormatTime(TimeSpan value, string? format)
+ {
+ var dateTime = DateTime.Today.Add(value);
+
+ try
+ {
+ return dateTime.ToString(string.IsNullOrWhiteSpace(format) ? "HH:mm" : format, INVARIANT_CULTURE);
+ }
+ catch (FormatException)
+ {
+ return dateTime.ToString("HH:mm", INVARIANT_CULTURE);
+ }
+ }
}
diff --git a/app/MindWork AI Studio/Plugins/assistants/README.md b/app/MindWork AI Studio/Plugins/assistants/README.md
index af0cb8b3..d985098e 100644
--- a/app/MindWork AI Studio/Plugins/assistants/README.md
+++ b/app/MindWork AI Studio/Plugins/assistants/README.md
@@ -17,6 +17,9 @@ This folder keeps the Lua manifest (`plugin.lua`) that defines a custom assistan
- [`BUTTON_GROUP` reference](#button_group-reference)
- [`SWITCH` reference](#switch-reference)
- [`COLOR_PICKER` reference](#color_picker-reference)
+ - [`DATE_PICKER` reference](#date_picker-reference)
+ - [`DATE_RANGE_PICKER` reference](#date_range_picker-reference)
+ - [`TIME_PICKER` reference](#time_picker-reference)
- [Prompt Assembly - UserPrompt Property](#prompt-assembly---userprompt-property)
- [Advanced Prompt Assembly - BuildPrompt()](#advanced-prompt-assembly---buildprompt)
- [Interface](#interface)
@@ -100,6 +103,9 @@ ASSISTANT = {
- `LAYOUT_ACCORDION_SECTION`: renders a `MudExpansionPanel`; requires `Name`, `HeaderText`, and may include `IsDisabled`, `IsExpanded`, `IsDense`, `HasInnerPadding`, `HideIcon`, `HeaderIcon`, `HeaderColor`, `HeaderTypo`, `HeaderAlign`, `MaxHeight`, `ExpandIcon`, `Class`, `Style`.
- `SWITCH`: boolean option; requires `Name`, `Label`, `Value`, and may include `OnChanged`, `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`.
+- `DATE_PICKER`: date input based on `MudDatePicker`; requires `Name`, `Label`, and may include `Value`, `Placeholder`, `HelperText`, `DateFormat`, `PickerVariant`, `UserPrompt`, `Class`, `Style`.
+- `DATE_RANGE_PICKER`: date range input based on `MudDateRangePicker`; requires `Name`, `Label`, and may include `Value`, `PlaceholderStart`, `PlaceholderEnd`, `HelperText`, `DateFormat`, `PickerVariant`, `UserPrompt`, `Class`, `Style`.
+- `TIME_PICKER`: time input based on `MudTimePicker`; requires `Name`, `Label`, and may include `Value`, `Placeholder`, `HelperText`, `TimeFormat`, `AmPm`, `PickerVariant`, `UserPrompt`, `Class`, `Style`.
- `PROVIDER_SELECTION` / `PROFILE_SELECTION`: hooks into the shared provider/profile selectors.
- `WEB_CONTENT_READER`: renders `ReadWebContent`; include `Name`, `UserPrompt`, `Preselect`, `PreselectContentCleanerAgent`.
- `FILE_CONTENT_READER`: renders `ReadFileContent`; include `Name`, `UserPrompt`.
@@ -119,6 +125,9 @@ Images referenced via the `plugin://` scheme must exist in the plugin directory
| `FILE_CONTENT_READER` | `Name` | `UserPrompt` | [`internal`](https://github.com/MindWorkAI/AI-Studio/blob/main/app/MindWork%20AI%20Studio/Components/ReadFileContent.razor) |
| `WEB_CONTENT_READER` | `Name` | `UserPrompt` | [`internal`](https://github.com/MindWorkAI/AI-Studio/blob/main/app/MindWork%20AI%20Studio/Components/ReadWebContent.razor) |
| `COLOR_PICKER` | `Name`, `Label` | `Placeholder`, `ShowAlpha`, `ShowToolbar`, `ShowModeSwitch`, `PickerVariant`, `UserPrompt`, `Class`, `Style` | [MudColorPicker](https://www.mudblazor.com/components/colorpicker) |
+| `DATE_PICKER` | `Name`, `Label` | `Value`, `Placeholder`, `HelperText`, `DateFormat`, `PickerVariant`, `UserPrompt`, `Class`, `Style` | [MudDatePicker](https://www.mudblazor.com/components/datepicker) |
+| `DATE_RANGE_PICKER` | `Name`, `Label` | `Value`, `PlaceholderStart`, `PlaceholderEnd`, `HelperText`, `DateFormat`, `PickerVariant`, `UserPrompt`, `Class`, `Style` | [MudDateRangePicker](https://www.mudblazor.com/components/daterangepicker) |
+| `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."](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) |
@@ -265,7 +274,7 @@ More information on rendered components can be found [here](https://www.mudblazo
- To update component state, return a table with a `fields` table.
- `fields` keys must reference existing component `Name` values.
- Supported write targets:
- - `TEXT_AREA`, single-select `DROPDOWN`, `WEB_CONTENT_READER`, `FILE_CONTENT_READER`, `COLOR_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
- `SWITCH`: boolean values
- Unknown field names and wrong value types are ignored and logged.
@@ -470,6 +479,109 @@ More information on rendered components can be found [here](https://www.mudblazo
}
```
+---
+
+### `DATE_PICKER` reference
+- Use `Type = "DATE_PICKER"` to render a MudBlazor date picker.
+- Required props:
+ - `Name`: unique state key used in prompt assembly and `BuildPrompt(input.fields)`.
+ - `Label`: visible field label.
+- Optional props:
+ - `Value`: initial date string. Use the same format as `DateFormat`; default recommendation is `yyyy-MM-dd`.
+ - `Placeholder`: hint text shown before a date is selected.
+ - `HelperText`: helper text rendered below the picker.
+ - `DateFormat`: output and parsing format; defaults to `yyyy-MM-dd`.
+ - `PickerVariant`: one of `Dialog`, `Inline`, `Static`; invalid or omitted values fall back to `Dialog`.
+ - `UserPrompt`: prompt context text for the selected date.
+ - `Class`, `Style`: forwarded to the rendered component for layout/styling.
+
+#### Example DatePicker component
+```lua
+{
+ ["Type"] = "DATE_PICKER",
+ ["Props"] = {
+ ["Name"] = "deadline",
+ ["Label"] = "Deadline",
+ ["Value"] = "2026-03-31",
+ ["Placeholder"] = "YYYY-MM-DD",
+ ["HelperText"] = "Pick the target completion date.",
+ ["DateFormat"] = "yyyy-MM-dd",
+ ["PickerVariant"] = "Dialog",
+ ["UserPrompt"] = "Use this as the relevant deadline."
+ }
+}
+```
+
+---
+
+### `DATE_RANGE_PICKER` reference
+- Use `Type = "DATE_RANGE_PICKER"` to render a MudBlazor date range picker.
+- Required props:
+ - `Name`: unique state key used in prompt assembly and `BuildPrompt(input.fields)`.
+ - `Label`: visible field label.
+- Optional props:
+ - `Value`: initial range string using ` - `, for example `2026-03-01 - 2026-03-31`.
+ - `PlaceholderStart`: hint text for the start date input.
+ - `PlaceholderEnd`: hint text for the end date input.
+ - `HelperText`: helper text rendered below the picker.
+ - `DateFormat`: output and parsing format for both dates; defaults to `yyyy-MM-dd`.
+ - `PickerVariant`: one of `Dialog`, `Inline`, `Static`; invalid or omitted values fall back to `Dialog`.
+ - `UserPrompt`: prompt context text for the selected date range.
+ - `Class`, `Style`: forwarded to the rendered component for layout/styling.
+
+#### Example DateRangePicker component
+```lua
+{
+ ["Type"] = "DATE_RANGE_PICKER",
+ ["Props"] = {
+ ["Name"] = "travelWindow",
+ ["Label"] = "Travel window",
+ ["Value"] = "2026-06-01 - 2026-06-07",
+ ["PlaceholderStart"] = "Start date",
+ ["PlaceholderEnd"] = "End date",
+ ["HelperText"] = "Select the full period.",
+ ["DateFormat"] = "yyyy-MM-dd",
+ ["PickerVariant"] = "Dialog",
+ ["UserPrompt"] = "Use this as the allowed date range."
+ }
+}
+```
+
+---
+
+### `TIME_PICKER` reference
+- Use `Type = "TIME_PICKER"` to render a MudBlazor time picker.
+- Required props:
+ - `Name`: unique state key used in prompt assembly and `BuildPrompt(input.fields)`.
+ - `Label`: visible field label.
+- Optional props:
+ - `Value`: initial time string. Use the same format as `TimeFormat`; default recommendations are `HH:mm` or `hh:mm tt`.
+ - `Placeholder`: hint text shown before a time is selected.
+ - `HelperText`: helper text rendered below the picker.
+ - `TimeFormat`: output and parsing format; defaults to `HH:mm`, or `hh:mm tt` when `AmPm = true`.
+ - `AmPm`: defaults to `false`; toggles 12-hour mode.
+ - `PickerVariant`: one of `Dialog`, `Inline`, `Static`; invalid or omitted values fall back to `Dialog`.
+ - `UserPrompt`: prompt context text for the selected time.
+ - `Class`, `Style`: forwarded to the rendered component for layout/styling.
+
+#### Example TimePicker component
+```lua
+{
+ ["Type"] = "TIME_PICKER",
+ ["Props"] = {
+ ["Name"] = "meetingTime",
+ ["Label"] = "Meeting time",
+ ["Value"] = "14:30",
+ ["Placeholder"] = "HH:mm",
+ ["HelperText"] = "Pick the preferred meeting time.",
+ ["TimeFormat"] = "HH:mm",
+ ["AmPm"] = false,
+ ["PickerVariant"] = "Dialog",
+ ["UserPrompt"] = "Use this as the preferred time."
+ }
+}
+```
+
## Prompt Assembly - UserPrompt Property
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:
@@ -481,7 +593,7 @@ user prompt:
```
-For switches the “value” is the boolean `true/false`; for readers it is the fetched/selected content; for color pickers it is the selected color text (for example `#FFAA00` or `rgba(...)`, depending on the picker mode). Always provide a meaningful `UserPrompt` so the final concatenated prompt remains coherent from the LLM’s perspective.
+For switches the “value” is the boolean `true/false`; for readers it is the fetched/selected content; for color pickers it is the selected color text (for example `#FFAA00` or `rgba(...)`, depending on the picker mode); for date and time pickers it is the formatted date, date range, or time string. Always provide a meaningful `UserPrompt` so the final concatenated prompt remains coherent from the LLM’s perspective.
## Advanced Prompt Assembly - BuildPrompt()
If you want full control over prompt composition, define `ASSISTANT.BuildPrompt` as a Lua function. When present, AI Studio calls it and uses its return value as the final user prompt. The default prompt assembly is skipped.
@@ -499,8 +611,11 @@ The function receives a single `input` Lua table with:
- 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 ` - ` 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`)
+ - `Type` (string, e.g. `TEXT_AREA`, `DROPDOWN`, `SWITCH`, `COLOR_PICKER`, `DATE_PICKER`, `DATE_RANGE_PICKER`, `TIME_PICKER`)
- `Label` (string, when provided)
- `UserPrompt` (string, when provided)
- `input.profile`: selected profile data
@@ -514,7 +629,7 @@ input = {
},
meta = {
[""] = {
- Type = "",
+ Type = "",
Label = "",
UserPrompt = ""
},
@@ -559,6 +674,12 @@ ASSISTANT.BuildPrompt = function(input)
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
@@ -875,6 +996,10 @@ Use `LAYOUT_ACCORDION` as the outer wrapper and put the actual content into one
- [Bitwise Operations Library](https://www.lua.org/manual/5.2/manual.html#6.7)
---
+> **Warning:** some common lua functions might not be available in this lua environment. Examples are:
+> 1. `tostring()`
+> 2. `pairs()`\\`ipairs()`
+
### Logging helpers
The assistant runtime exposes basic logging helpers to Lua. Use them to debug custom prompt building.
diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua
index 94681438..14097de6 100644
--- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua
+++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua
@@ -343,6 +343,53 @@ ASSISTANT = {
["UserPrompt"] = "",
}
},
+ {
+ ["Type"] = "DATE_PICKER",
+ ["Props"] = {
+ ["Name"] = "", -- required
+ ["Label"] = "", -- required
+ ["Value"] = "2026-03-16", -- optional initial value
+ ["Placeholder"] = "YYYY-MM-DD",
+ ["HelperText"] = "",
+ ["DateFormat"] = "yyyy-MM-dd",
+ ["PickerVariant"] = "