Violet

Menu

On this page

The core.ui module lets scripts build their own settings UI in the web menu. Every constructor takes a single options table and returns an element handle you can read, write, and mutate at runtime. Settings persist across sessions and are also reachable through core.get_setting() / core.set_setting().

lua
local enabled = core.ui.checkbox{ id = "enabled", label = "Enable", panel = "General", default = false }

function on_tick()
  if enabled:get() then
    -- ...
  end
end
IDs, tabs & sandboxing
  • IDs are namespaced per script as lua.<script>.<id> (e.g. id = "enabled" in my_script.lua becomes lua.my_script.enabled). An ID you pass that already starts with lua. is kept as-is. This keeps scripts from colliding with each other or with core settings. Prefer the handle (elem:get()) so you never have to type the full ID; if you use core.get_setting, pass the full namespaced ID.
  • Custom tabs are allowed. Omit tab to land in the shared Lua tab, or pass any name to create your own top-level tab. Reserved/core tab names (autos, combat, hacks, items, map, macros, keybinds, packets, debug, settings, player, admin) are rejected. Give your tabs an icon or fold several into a submenu with core.ui.tab.
  • Idempotent. Re-running a constructor with an existing ID returns a handle to the existing element instead of creating a duplicate, so it's safe to build your UI at the top of the script.

Common options

Every constructor accepts these keys in its options table:

KeyTypeNotes
idstringRequired. Namespaced to lua.<script>.<id>.
labelstringDisplay label.
panelstringRequired. Card within the tab.
tabstringTop-level tab. Defaults to "Lua".
sectionstringIn-page sub-tab this panel belongs to (see Organizing your UI).
tooltipstringHover text.
orderintSort order within the panel (lower first).
descriptionstringPanel subtitle (card description).
visible_whenstringID of another element; this one is hidden unless that element is truthy. Namespaced like id.
hiddenboolStart hidden (toggle later with :set_visible).
disabledboolStart non-interactive (toggle later with :set_enabled).
dangerousboolHidden unless "Show Dangerous" is on.
transientboolNever saved to profiles (runtime-only).
criticalbool | stringMark safety-critical; a string overrides the alert label.

Creating widgets

core.ui.checkbox(opts) -> element

Boolean toggle. Extra option: default (bool).

lua
local cb = core.ui.checkbox{ id = "enabled", label = "Enable", panel = "General", default = false }

core.ui.slider_int(opts) -> element

Integer slider. Extra options: default, min, max, step (defaults: 0 / 0 / 100 / 1).

lua
local spd = core.ui.slider_int{ id = "speed", label = "Speed", panel = "General",
                                default = 50, min = 0, max = 100, step = 5 }

core.ui.slider_float(opts) -> element

Float slider. Extra options: default, min, max, step, rounding (defaults: 0 / 0 / 1 / 0.1 / 2 decimals).

lua
local r = core.ui.slider_float{ id = "ratio", label = "Ratio", panel = "General",
                                default = 1.0, min = 0.0, max = 5.0, step = 0.1, rounding = 2 }

core.ui.dropdown(opts) -> element

Single-select dropdown. Extra options: options (array of strings), default (0-based index). Its value is the selected index.

lua
local target = core.ui.dropdown{ id = "target", label = "Target", panel = "Targeting",
                                 options = { "Closest", "Random" }, default = 0 }

core.ui.radio_group(opts) -> element

Like a dropdown but rendered as a row of buttons. Extra options: options, default (index). Value is the selected index.

lua
local mode = core.ui.radio_group{ id = "mode", label = "Mode", panel = "General",
                                  options = { "Safe", "Fast", "YOLO" }, default = 0 }

core.ui.multi_select(opts) -> element

Listbox where several options can be checked. Extra options: options, default (array of indices). Value is a sorted array of selected indices.

lua
local kinds = core.ui.multi_select{ id = "kinds", label = "Hit Kinds", panel = "Targeting",
                                    options = { "Mobs", "Reactors", "NPCs" }, default = { 0 } }

core.ui.input_text(opts) -> element

Text field. Extra options: default (string), flags (array of strings, passed to the renderer).

lua
local note = core.ui.input_text{ id = "note", label = "Note", panel = "General", default = "" }

core.ui.keybind(opts) -> element

Key capture. Extra option: default (key name string, e.g. "F8").

lua
local key = core.ui.keybind{ id = "toggle_key", label = "Toggle Key", panel = "General", default = "F8" }

core.ui.color(opts) -> element

RGBA color picker. Extra option: default (array of 4 floats { r, g, b, a }, each 0–1).

lua
local col = core.ui.color{ id = "tint", label = "Tint", panel = "General", default = { 1, 0, 0, 1 } }

core.ui.button(opts) -> element

One-shot action button — no value; poll it with :pressed() (see Buttons). Extra options: variant ("default", "destructive", "outline", "secondary", "ghost"), confirm (confirmation prompt shown before firing).

lua
local run = core.ui.button{ id = "run", label = "Delete", panel = "Actions", variant = "destructive", confirm = "Are you sure?" }

core.ui.static_text(opts) -> element

Display-only text (no value). Extra options: text (the content), style ("muted", "body", or "heading"), markdown (when true, render inline **bold**, *italic*, `code`). Update it later with :set_text().

lua
local status = core.ui.static_text{ id = "status", panel = "Actions", style = "body", text = "idle" }

core.ui.progress(opts) -> element

Read-only progress bar. Extra options: value, min, max (defaults 0 / 0 / 1), display ("percent" or "value"), color ("violet", "success", "warning", "danger", "info"). Drive it from script with :set_value().

lua
local bar = core.ui.progress{ id = "bar", label = "Progress", panel = "Actions", value = 0, min = 0, max = 100, color = "success" }

core.ui.separator(opts) -> element

Visual divider. With a label it renders as a small section heading; without one, a plain rule.

lua
core.ui.separator{ id = "sec1", label = "Advanced", panel = "General" }

Organizing your UI

Bigger scripts quickly outgrow one flat page. Three tools keep the menu tidy: custom tab icons, nav groups (submenus), and in-page sections.

core.ui.tab(opts)

Sets navigation metadata for one of your tabs. Call it once at the top of the script — order relative to element creation doesn't matter, but the tab only shows up once at least one element is placed on it. Returns nothing.

KeyTypeNotes
namestringRequired. The tab this metadata applies to (the same string you pass as tab).
iconstringSidebar icon — a kebab-case name from the icon set.
groupstringNav group label. Your tabs sharing the same group fold into one collapsible sidebar entry.
group_iconstringIcon for the group header. Among grouped tabs, the first one that sets it wins.
orderintSort order among script tabs / within the group (lower first).

Reserved tab names are rejected, plus the shared Lua tab (you can place elements there, but not restyle a surface every script shares). Groups only apply to script tabs — built-in tabs can't be regrouped or re-iconed.

lua
local GROUP = "Azura's Scripts"

core.ui.tab{ name = "AIO Grinding",     group = GROUP, group_icon = "crown", icon = "swords", order = 1 }
core.ui.tab{ name = "AIO Monster Park", group = GROUP, icon = "castle", order = 2 }
core.ui.tab{ name = "Secret Exploits",  group = GROUP, icon = "skull",  order = 3 }

core.ui.checkbox{ id = "enabled", label = "Enable", tab = "AIO Grinding", panel = "General", default = false }
-- ...

The sidebar now shows a single collapsible Azura's Scripts entry with the three tabs inside it, instead of three flat nav items.

In-page sections (tab strips)

Pass section on elements to split a busy page into sub-tabs, like the built-in Player page's Equip / Use / Set-up / Etc / Cash inventory tabs. Panels that share a section render under one tab strip; panels without a section stay pinned above the strip — put your master toggle there.

lua
local TAB = "AIO Grinding"

-- No section: always visible above the strip.
local enabled = core.ui.checkbox{ id = "enabled", label = "Enable", tab = TAB, panel = "General", default = false }

-- Buffs | Skills | Misc
core.ui.checkbox{ id = "haste", label = "Haste", tab = TAB, section = "Buffs", panel = "Party Buffs", default = true }
core.ui.checkbox{ id = "hs", label = "Holy Symbol", tab = TAB, section = "Buffs", panel = "Party Buffs", default = true }
core.ui.slider_int{ id = "combo", label = "Combo Count", tab = TAB, section = "Skills", panel = "Attacking",
                    default = 3, min = 1, max = 10 }
core.ui.checkbox{ id = "loot", label = "Auto Loot", tab = TAB, section = "Misc", panel = "Other", default = true }

Sections appear in the order they are first used. Keep every element of one panel in the same section, and move elements later with elem:set_section(). Sections are purely visual — IDs, values, and saved profiles are unaffected.

Icon set

icon and group_icon accept these kebab-case names (a curated subset of lucide.dev). Unknown names quietly fall back to the default icon, so typos never break a menu.

activity, alarm-clock, anchor, archive, award, axe, backpack, banknote, bell, bird, bomb, book, book-open, bot, braces, brain, brush, bug, calendar, camera, car, castle, circle-dollar-sign, clock, cloud-lightning, code-2, cog, coins, compass, cpu, crosshair, crown, database, diamond, dices, dna, droplet, egg, eye, eye-off, feather, file-code, filter, fish, flag, flame, flask-conical, footprints, gamepad-2, gauge, gem, ghost, gift, glasses, hammer, hash, heart, hourglass, house, infinity, key, landmark, layers, layout-grid, leaf, link, list, lock, magnet, mail, map, map-pin, medal, message-square, moon, mountain, music, navigation, package, palette, pause, paw-print, pen, percent, pickaxe, pin, plane, play, puzzle, rabbit, refresh-cw, repeat, rocket, route, ruler, scale, scissors, scroll, scroll-text, search, send, settings, shield, shield-check, ship, shopping-cart, skull, sliders-horizontal, snowflake, sparkles, sprout, star, store, sun, sword, swords, table, target, tent, terminal, test-tube, timer, trending-up, trees, trophy, truck, umbrella, unlock, user, users, wallet, wand-2, watch, waves, wheat, wrench, zap

The element handle

Every constructor returns a handle. Methods re-resolve the element each call, so a handle stays safe even if the element is later removed (calls become no-ops). Mutator methods return the handle, so they can be chained.

element:get() -> any

Returns the current value (bool / number / string / array, depending on widget type).

lua
if cb:get() then core.log("on") end

element:set(value) -> boolean

Sets the value, coerced to the element's type, and pushes it to the web menu.

lua
spd:set(75)

element:id() -> string

Returns the full namespaced ID (e.g. lua.my_script.enabled).


element:set_label(label) -> element

Changes the display label at runtime.


element:set_tooltip(text) -> element

Changes the tooltip at runtime.


element:set_visible(visible) -> element

Shows or hides the element. Hidden elements are removed from the layout.

lua
spd:set_visible(mode:get_index() ~= 2)

element:visible() -> boolean

Returns whether the element is currently visible.


element:set_enabled(enabled) -> element

Enables or disables interaction (the element stays visible but greys out).


element:enabled() -> boolean

Returns whether the element is currently enabled.


element:set_visible_when(other_id) -> element

Binds conditional visibility to another element's value at runtime.


element:set_order(order) -> element

Changes the sort order within the panel.


element:set_panel(panel) -> element

Moves the element to a different panel.


element:set_section(section) -> element

Moves the element to a different in-page section (see Organizing your UI). Pass "" to detach it from the strip and pin it above.


element:set_options(options) -> element

Replaces the option list of a dropdown, radio_group, or multi_select at runtime. The selected index is clamped to the new range.

lua
target:set_options({ "Closest", "Random", "Boss", "Elite" })

element:get_index() -> number

Returns the selected index of a dropdown or radio_group.


element:set_index(index) -> element

Sets the selected index of a dropdown or radio_group.


element:get_selected() -> any

For dropdown/radio_group returns the selected option string; for multi_select returns the array of selected indices.


element:set_selected(indices) -> element

Sets the selected indices of a multi_select (array of indices).


element:set_min(value) -> element

Changes a slider's minimum at runtime (value is re-clamped).


element:set_max(value) -> element

Changes a slider's maximum at runtime (value is re-clamped).


element:set_step(value) -> element

Changes a slider's step at runtime.


element:set_text(text) -> element

Changes the text of a static_text element.


element:set_value(value) -> element

Sets the current value of a progress bar.


element:pressed() -> boolean

For a button: returns true exactly once after each click, then clears. Poll it in on_tick.


element:remove()

Removes the element from the menu.

Buttons

There is no UI event callback system; buttons are polled. :pressed() returns true once per click and then resets, so check it inside on_tick:

lua
local run = core.ui.button{ id = "run", label = "Run Once", panel = "Actions" }

function on_tick()
  if run:pressed() then
    core.log("button clicked")
  end
end

Widget reference

ConstructorValue (:get())Extra optionsKey methods
checkboxbooldefault:get :set
slider_intnumberdefault min max step:get :set :set_min :set_max :set_step
slider_floatnumberdefault min max step roundingsame as slider_int
dropdownindexoptions default:get_index :set_index :get_selected :set_options
radio_groupindexoptions defaultsame as dropdown
multi_selectindex arrayoptions default:get_selected :set_selected :set_options
input_textstringdefault flags:get :set
keybindstringdefault:get :set
color{r,g,b,a}default:get :set
buttonvariant confirm:pressed
static_texttext style markdown:set_text
progressnumbervalue min max display color:set_value
separator

Full example

lua
plugin = {
    name = "UI Demo",
    version = "1.0.0",
    author = "Author",
    description = "Showcases the core.ui API",
    load = true
}

local TAB = "UI Demo"

local enabled = core.ui.checkbox{ id = "enabled", label = "Enable", tab = TAB, panel = "General", default = true }
local mode    = core.ui.radio_group{ id = "mode", label = "Mode", tab = TAB, panel = "General",
                                     options = { "Safe", "Fast", "YOLO" }, default = 0 }
local speed   = core.ui.slider_int{ id = "speed", label = "Speed", tab = TAB, panel = "General",
                                    default = 50, min = 0, max = 100, step = 5 }
local target  = core.ui.dropdown{ id = "target", label = "Target", tab = TAB, panel = "Targeting",
                                  options = { "Closest", "Random" }, default = 0 }
local status  = core.ui.static_text{ id = "status", tab = TAB, panel = "Actions", style = "body", text = "idle" }
local bar     = core.ui.progress{ id = "bar", label = "Progress", tab = TAB, panel = "Actions", value = 0, min = 0, max = 100 }
local run     = core.ui.button{ id = "run", label = "Run Once", tab = TAB, panel = "Actions" }

local running, ticks = false, 0

function on_tick()
    if run:pressed() and enabled:get() then
        running, ticks = true, 0
        status:set_text("running (mode=" .. mode:get_selected() .. ", target=" .. target:get_selected() .. ")")
        target:set_options({ "Closest", "Random", "Boss", "Elite" })  -- grow the list at runtime
    end

    if running then
        ticks = ticks + 1
        bar:set_value(math.min(ticks, 100))
        if ticks >= 100 then
            running = false
            status:set_text("done")
        end
    end

    speed:set_visible(mode:get_index() ~= 2)  -- hide Speed in YOLO mode
end

Legacy: core.menu

The original core.menu functions still work — they are thin wrappers over core.ui that force the Lua tab and the legacy lua.<id> naming (no per-script segment). They return a boolean instead of a handle, so to read/write their values you use core.get_setting / core.set_setting with the full lua.<id> path. Prefer core.ui for new scripts.

core.menu.create_checkbox(id, label, tab, panel, default_value [, tooltip]) -> boolean

Creates a checkbox setting (the tab argument is ignored — always "Lua").

lua
core.menu.create_checkbox("myScript.enabled", "Enable Feature", "Lua", "My Script", false)
-- Registered as id="lua.myScript.enabled"; read with core.get_setting("lua.myScript.enabled")

core.menu.create_slider_int(id, label, tab, panel, default, min, max [, step [, tooltip]]) -> boolean

Creates an integer slider setting. step defaults to 1.


core.menu.create_slider_float(id, label, tab, panel, default, min, max [, step [, tooltip [, rounding]]]) -> boolean

Creates a float slider setting. step defaults to 0.1, rounding defaults to 2.


core.menu.create_dropdown(id, label, tab, panel, options, default_index [, tooltip]) -> boolean

Creates a dropdown setting. options is a Lua array of strings; default_index is 0-based.


core.menu.create_keybind(id, label, tab, panel [, default_key [, tooltip]]) -> boolean

Creates a keybind setting.


core.menu.create_input_text(id, label, tab, panel [, default_value [, tooltip]]) -> boolean

Creates a text input setting.