Configuration UI
Declare the settings page users see when they install your module.
Every module needs settings: API keys, endpoint URLs, model choices, toggles, sliders. Voxta renders the settings UI for you — you declare the fields, Voxta handles persistence, validation, and rendering.
The pattern
There are three pieces:
- A typed settings record (
Models/MySettings.cs) — your in-code representation, plain init-only properties. - A
ModuleConfigurationProvider(Configuration/ModuleConfigurationProvider.cs) — declares the form fields the UI renders, and maps form data into your typed record. - The registration in
VoxtaModule.cs— points the framework at your provider.
builder.Register(new ServiceDefinition
{
// ...
ModuleConfigurationProviderType = typeof(ModuleConfigurationProvider),
ModuleConfigurationFieldsRequiringReload = ModuleConfigurationProvider.FieldsRequiringReload
});The settings record
public record MySettings
{
public string ApiKey { get; init; } = "";
public string Endpoint { get; init; } = "https://api.example.com";
public bool EnableFeatureX { get; init; } = true;
public int MaxRetries { get; init; } = 3;
}This is the only type your business logic should see. Don't pass ISettingsSource around — convert it once, then work with the typed record.
The provider
public class ModuleConfigurationProvider : ModuleConfigurationProviderBase, IModuleConfigurationProvider
{
public static readonly FormPasswordField ApiKeyField = new()
{
Name = "ApiKey",
Label = "API key",
Text = "Get yours at https://example.com/account."
};
public static readonly FormTextField EndpointField = new()
{
Name = "Endpoint",
Label = "Endpoint URL",
DefaultValue = "https://api.example.com"
};
public static readonly FormBooleanField EnableFeatureXField = new()
{
Name = "EnableFeatureX",
Label = "Enable feature X",
DefaultValue = true
};
public static readonly FormIntField MaxRetriesField = new()
{
Name = "MaxRetries",
Label = "Max retries",
DefaultValue = 3,
Minimum = 0,
Maximum = 10
};
public static MySettings ToSettings(ISettingsSource settings) => new()
{
ApiKey = settings.GetRequired(ApiKeyField),
Endpoint = settings.GetRequired(EndpointField),
EnableFeatureX = settings.GetRequired(EnableFeatureXField),
MaxRetries = settings.GetRequired(MaxRetriesField)
};
public static readonly string[] FieldsRequiringReload =
[
ApiKeyField.Name,
EndpointField.Name
];
public Form GetForm() => new Form
{
Fields =
[
ApiKeyField,
EndpointField,
EnableFeatureXField,
MaxRetriesField
]
};
}Form field catalog
The framework provides typed field classes for every common UI input. Pick the one that matches your data:
| Field type | UI | Use for |
|---|---|---|
FormTextField | Single-line text | URLs, names, free text |
FormPasswordField | Masked input | API keys, secrets |
FormMultilineField | Multi-line text area | Prompts, long config |
FormBooleanField | Toggle / checkbox | On/off toggles |
FormIntField | Number input | Integer counts, ports |
FormDoubleField | Number input | Decimal values (temperature, scales) |
FormChoicesField | Dropdown (single select) | Fixed list of options |
FormMultipleChoicesField | Multi-select | Checkbox list |
FormEnumField | Dropdown from a C# enum | When the values are an enum |
FormMultipleEnumField | Multi-select from a C# enum | Flag-like enum selections |
FormModelField | Model picker | LLM / TTS / vision model selection |
FormAssetField | Asset picker | File reference (character image, voice sample) |
FormStringListField | Editable string list | URLs, keyword lists |
FormIntListField | Editable int list | Port ranges, IDs |
FormArrayOfObjectsField | Repeatable nested object | List of configured items, each with multiple fields |
FormTitleField | Section heading | Visual grouping in the UI |
FormDocumentationField | Markdown block | Inline help text, links, warnings |
FormInvokeActionField | Button | Triggers a server-side action (test connection, refresh model list) |
FormConnectionField | Connection picker | Reuses a saved connection (e.g. a shared API key) |
FormHiddenField | (hidden) | Persisted state not shown to the user |
FormAuthorizeField | OAuth flow | Browser-redirect-style authorization |
FormLoraListField | LoRA selector | Stable Diffusion LoRA configuration |
Every field has at minimum Name (must match a setting in your record) and Label. Most support Text (help text), DefaultValue, and validation properties (Minimum, Maximum, Required, etc.).
Required vs optional
ApiKey = settings.GetRequired(ApiKeyField); // throws if not set
Endpoint = settings.GetOptional(EndpointField); // returns null if not setGetRequired is checked at form-validation time — users can't save without a value. Use it for anything your code can't run without. GetOptional is for genuinely optional fields.
FieldsRequiringReload
If a user changes a value in a field listed here, Voxta tears down your service and re-initializes it. Use it for anything that affects long-lived state the service set up at init:
- API endpoints or keys (the HTTP client is rebuilt)
- File paths the service watches
- Anything passed to a singleton dependency
Don't include fields whose values are read fresh on every chat instance — those don't need a reload.
Reference
The voxta-module-elite-dangerous source declares over 70 fields across booleans, sliders, dropdowns, scan-code mappings, and section titles. Read it as a worked example covering most field types.