Configuration manager
Named local JSON profiles, explicit flags and silent restoration.
Configure before registering flags#
local window = Aroyn:CreateWindow({
Parent = parent, Id = "preferences",
Config = {Namespace = "my-game", AutoLoad = false, AutoSave = false},
})
local section = window:CreatePage({Name = "Settings", Icon = "settings"})
:CreateSection({Title = "Preferences"})
local enabled = section:CreateToggle({Name = "Enabled", Flag = "enabled", Default = false})
section:CreateTextInput({Name = "Private value", Flag = "private", Sensitive = true})
local ok, info = window:SaveConfig("main")
if not ok then warn(info.Code, info.Message) end
window:LoadConfig("main") -- silent by default
Open full size ↗Options#
| Option | Type | Default | Meaning |
|---|---|---|---|
| Namespace | string | "default" | Safe lowercase application/game identifier. |
| FileSystem | table? | nil | Synchronous adapter; nil selects available executor globals. |
| DefaultConfig | string? | nil | Fallback name for AutoLoad=true and AutoSaveConfig. |
| AutoLoad | boolean | string | false | true loads DefaultConfig or main; a string loads that name. |
| AutoSave | boolean | false | Opt-in debounced writes after changes. |
| AutoSaveConfig | string? | DefaultConfig or "main" | Independent of the last profile loaded. |
| AutoSaveDelay | finite number | 0.75 | 0.2–60 seconds. |
ConfigureConfig can initialize an existing window before any Flag. The first registration otherwise creates the default manager. A second ConfigureConfig returns AlreadyConfigured. Invalid configuration options return InvalidOptions; invalid Config passed during CreateWindow/AttachWindow can assert.
Public methods#
| Method | Returns | Behavior |
|---|---|---|
| ConfigureConfig(options?) | ok, info | Before the first Flag; cannot reconfigure. |
| RegisterConfig(control, flag, options?) | control | Register an existing owned control; options Persist/Sensitive. |
| GetConfigState() | table | Independent JSON-compatible snapshot; does not write a file. |
| GetConfigStatus() | table | Mode, Namespace, Path, Capabilities, AutoSave, AutoSaveConfig, AutoSaveDelay, LastResult. |
| SaveConfig(name) | ok, info | Save current registered values and valid deferred entries. |
| LoadConfig(name, options?) | ok, info | Restore; InvokeCallbacks=false by default. |
| ListConfigs() | ok, info | Sorted names in info.Names. |
| DeleteConfig(name) | ok, info | Delete profile; cancel pending save for its name. |
| ResetConfig(options?) | ok, info | Restore registered defaults, clear deferred; saved files remain. |
| SetConfigAutoSave(enabled, name?, delay?) | ok, info | Enable/disable; enabling does not write immediately. |
Flags and exclusion#
Flag must be unique per Window, not per section: 1–64 ASCII letters/digits/underscore/hyphen, starting with a letter or digit. Name and construction order are not identifiers. Without a Flag, a value is not saved. RegisterConfig captures the default at registration.
Persist=false or Sensitive=true excludes that flag, including imported entries. JSON is plain text without encryption. No automatic token detection exists. Declare sensitive controls before loading; unknown valid flags are deferred until the library learns their type and exclusion policy.
Storage and schema#
File paths are relative to the executor workspace: AroynUILab/configs/<namespace>/<name>.json. Names and namespaces normalize to lowercase. Paths, extensions, periods, spaces and Windows device names are rejected. This format does not import old production Hub settings.
{
"SchemaVersion": 1,
"Namespace": "my-game",
"Values": {
"enabled": {"Type": "Toggle", "Value": true},
"volume": {"Type": "Slider", "Value": 75},
"mode": {"Type": "Dropdown", "Value": "Quiet"},
"categories": {"Type": "Dropdown", "Multi": true, "Value": ["Pets"]},
"note": {"Type": "Input", "Value": "Local note"},
"shortcut": {"Type": "Keybind", "Value": {"Key": "H", "Mode": "Hold"}},
"color": {"Type": "ColorPicker", "Value": {"R": 0.4, "G": 0.5, "B": 0.6}}
}
}Only SchemaVersion=1 is accepted. An empty single choice uses Value=false, a multi uses an empty array/table. Color channels are finite 0–1 numbers. Keybind stores the enum name or "None" and mode; it excludes active/pressed state.
Load and validation#
A missing, empty, corrupt, oversized (>256 KiB), wrong-namespace or unsupported-schema document returns an error without changing the UI. Up to 512 flags are supported. An invalid individual value is skipped with warnings; absent values retain current state. Save rejects invalid or oversized snapshots before writing.
Slider values clamp/quantize. Multi choices filter duplicates and removed options. A removed single choice falls back to an available construction default or nil. Invalid RGB, keys, modes, types and numbers are skipped. Text is limited to 16 KiB of UTF-8 bytes.
Valid unknown flags remain deferred for later registrations and round-trip through Save. A new Load replaces old deferred entries; Reset clears them. Load returns Applied, Deferred, Skipped and Warnings. Callbacks are silent unless InvokeCallbacks=true; activation is still suppressed. Changed controls receive at most one ordinary callback.
Filesystem capabilities#
| Capability | Behavior |
|---|---|
| readfile + writefile | File mode based on presence, not proof of successful I/O. |
| Either missing | Session mode: in-memory snapshots for this window only. Destroy or rerun loses them. |
| isfile / isfolder / makefolder | Used when available. Without folder APIs the directory must already exist. |
| listfiles | Enumerates relative or absolute paths. Without it, the manager uses __index.json. |
| delfile | Required for deletion in File mode; otherwise DeleteUnsupported. |
FileSystem supplies plain synchronous functions, not methods requiring self. readfile(path) returns a string or throws; writefile(path, json), makefolder(path) and delfile(path) must throw on failure. Returning false from a custom writefile is not treated as an error. listfiles(directory) returns a table of path strings.
AutoSave and AutoLoad#
window:SetConfigAutoSave(true, "main", 0.75)
-- Only later changes schedule a write. Loading "other" does not
-- silently change the AutoSave destination.
local status = window:GetConfigStatus()
print(status.Mode, status.AutoSaveConfig)
if status.LastResult then print(status.LastResult.Code) endAutoSave uses one debounce task per Window. Text edits and slider/color drags update pending state without writing every event. Manual Save, Load, Reset, disabling AutoSave and Destroy cancel the pending task. Destroy does not perform a hidden final flush. Check LastResult for automatic failures.
AutoLoad runs when the manager is created, before flags, and restores deferred values on registration. Check GetConfigStatus().LastResult immediately: ConfigureConfig returning Configured is not proof that the load succeeded. Explicit Load after constructing controls is easier to handle reliably.
Results and errors#
Operations normally return ok plus info containing Success, Code, Message and Mode. Success codes include Configured, Saved, Loaded, Listed, Deleted, Reset and AutoSaveChanged. A pre-manager invalid options result may not contain Mode.
| Error family | Codes |
|---|---|
| Arguments / state | InvalidOptions, AlreadyConfigured, InvalidName, InvalidState, EncodeFailed |
| Document | Missing, Empty, TooLarge, Corrupt, SchemaUnsupported, InvalidDocument |
| Filesystem | ReadFailed, WriteFailed, FolderFailed, ListFailed, DeleteUnsupported, DeleteFailed |
| Index / operation | IndexInvalid, IndexWriteFailed, Busy, OperationFailed |
A partial index error can include FileSaved=true or FileDeleted=true. Inspect the full result before retrying. Do not disguise an I/O error as a successful Session-mode operation.