> ## Documentation Index
> Fetch the complete documentation index at: https://lua.starline.one/llms.txt
> Use this file to discover all available pages before exploring further.

# UI

> Create tabs, groups, controls, lists, text inputs, and dynamic menu layouts.

Load-time only. Calling `ui.*` from a callback errors.

## Tabs and groups

Elements can be organized into tabs and groups.

```lua theme={"dark"}
local general = ui.Tab("general")
local trigger = general.Group("triggerbot")
local enabled = trigger.Checkbox("enabled", true)

local advanced = ui.Tab("advanced")
advanced.Group("filtering").Checkbox("head only", false)
```

These tabs appear inside your script's page, not in the main menu bar.

Members take a **dot**. Groups lay out across two columns in declaration order.

Skip either level and it falls back to something named after your script:

```lua theme={"dark"}
local g = ui.Group("misc")      -- no tab -> auto tab
local loose = ui.Checkbox("x")  -- no tab, no group -> auto tab, auto group
```

The tab bar hides itself when there is only one tab.

## Elements

Every element exists on `ui` and on every group handle.

| Function                                                             | Returns      |                                                                        |
| -------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------- |
| `Tab(name)`                                                          | tab handle   | only `Group` exists on it                                              |
| `Group(name)`                                                        | group handle |                                                                        |
| `Checkbox(name, default)`                                            | handle       |                                                                        |
| `SliderInt(name, min, max[, default])`                               | handle       | `default` falls back to `min`; `min` must be less than `max`           |
| `SliderFloat(name, min, max[, default])`                             | handle       | same                                                                   |
| `Combo(name, items[, default])`                                      | handle       | `default` is a 0-based index into `items`, defaults to `0`             |
| `MultiCombo(name, items)`                                            | handle       | all items start unselected                                             |
| `ListBox(name, items[, rows])`                                       | handle       | `items` may be empty; `rows` defaults to `6` and is clamped to `1..32` |
| `TextInput(name[, default[, placeholder]])`                          | handle       | `placeholder` shows while the field is empty                           |
| `Button(name, on_click)`                                             | handle       |                                                                        |
| `ColorPicker(name, r, g, b[, a])` or `ColorPicker(name, Color(...))` | handle       | `a` defaults to `255`                                                  |
| `Label(text)`                                                        | handle       | no value; only `SetVisible`                                            |
| `Divider()`                                                          | handle       | same                                                                   |

Every element except `TextInput` gets a hotkey automatically. There is no `Hotkey` element.

```lua theme={"dark"}
local aimbot = ui.Tab('general').Group('aimbot')

local enabled = aimbot.Checkbox('enabled', true)
local delay   = aimbot.SliderInt('delay (ms)', 0, 250, 35)
local fov     = aimbot.SliderFloat('fov', 0.0, 180.0, 45.5)
local team    = aimbot.Combo('target team', { 'enemies', 'teammates', 'all' }, 0)
local hitbox  = aimbot.MultiCombo('hitboxes', { 'head', 'chest', 'stomach' })
local col     = aimbot.ColorPicker('chams', 255, 0, 0)
local tag     = aimbot.TextInput('clantag', '', 'starline')

aimbot.Button('reset', function() print('clicked') end)
aimbot.Label('experimental')
aimbot.Divider()
```

## Handles

`Get` and `Set` take a **colon**.

```lua theme={"dark"}
local v = enabled:Get()
enabled:Set(false)
```

| Element                      | `Get()` returns                                     | `Set(...)` takes                     |
| ---------------------------- | --------------------------------------------------- | ------------------------------------ |
| `Checkbox`                   | Boolean                                             | Boolean                              |
| `SliderInt`                  | Integer                                             | Number, clamped to `min..max`        |
| `SliderFloat`                | Number                                              | Number, clamped to `min..max`        |
| `Combo`                      | Integer, 0-based index                              | Integer, clamped                     |
| `MultiCombo`                 | `Get(index)`, Boolean. 0-based, errors out of range | `Set(index, bool)`, 0-based          |
| `ListBox`                    | Integer, 0-based index; `-1` when empty             | Integer, clamped                     |
| `TextInput`                  | String                                              | String, max 512 chars                |
| `ColorPicker`                | [`Color`](/color)                                   | `Set(Color)` or `Set(r, g, b [, a])` |
| `Button`, `Label`, `Divider` | nothing                                             | not supported                        |

At load time, `Get()` returns the declared default. Restored values are applied after loading.

## Lists

`ListBox` is an always-open scrolling list with one selected row. `handle:SetItems(table)` replaces the whole list from any callback — that is how you build a list that grows.

```lua theme={"dark"}
local nades = {}
local list  = ui.ListBox('nades', nades, 8)

ui.Button('add', function()
    nades[#nades + 1] = 'nade ' .. #nades
    list:SetItems(nades)
end)

ui.Button('remove', function()
    local i = list:Get()
    if i >= 0 then
        table.remove(nades, i + 1)   -- Get is 0-based, table.remove is 1-based
        list:SetItems(nades)
    end
end)
```

`SetItems` accepts only strings. Selection stays at the same index when possible, clamps when the list shrinks, and becomes `-1` when empty.

## Text

`TextInput` is a one-line field. Read it with `Get`, or take `handle:OnSubmit(fn)` to be called when the user presses enter or leaves the field.

```lua theme={"dark"}
local tag = ui.TextInput('clantag', '', 'type a tag')

tag:OnSubmit(function(text)
    print(text)
end)
```

`OnSubmit` returns the handle for chaining. Pass `nil` to remove the handler. It can be called from callbacks.

## Showing and hiding

`handle:SetVisible(bool)` on any handle, including `Label` and `Divider`. Siblings reflow, the group shrinks. Call it from a [`paint`](/events) callback to drive it off another element:

```lua theme={"dark"}
local mode  = ui.Combo('mode', { 'off', 'legit', 'rage' }, 0)
local speed = ui.SliderInt('speed', 1, 10, 5)

events.On('paint', function()
    speed:SetVisible(mode:Get() == 2)   -- rage only
end)
```

Elements start visible. Hiding an element does not change its value.

## Built-in controls

`ui.Get` and `ui.Set` read and write the cheat's own controls (not the ones your script created, those use the handle above).

```lua theme={"dark"}
local on = ui.Get("aimbot.legit.triggerbot.enabled")
ui.Set("aimbot.legit.triggerbot.enabled", true)
ui.Set("aimbot.legit.weapons.max fov", 3.5)
ui.Set("visuals.world.modulation.world.color", Color(255, 0, 0))
```

Both error if no control matches the path.

| Control      | Value                                     |
| ------------ | ----------------------------------------- |
| checkbox     | boolean                                   |
| slider       | number                                    |
| combo        | number, 0-based index                     |
| multi-combo  | array of booleans                         |
| keybind      | number, virtual-key code                  |
| color picker | [`Color`](/color), or a `{r,g,b,a}` table |

### Buttons

Buttons have no value. Use `ui.Click(path)` to activate one.

```lua theme={"dark"}
ui.Click("visuals.world.view.viewmodel changer.reset")
```

| Returns |                                                                |
| ------- | -------------------------------------------------------------- |
| `true`  | the button fired                                               |
| `false` | the control is greyed out, same as the menu refusing the click |

Errors if nothing matches the path, or if the path matches a control that is not a button. Works on script buttons too, by their path in the scripts tab.

The button callback runs on the thread that called `ui.Click`.

Color pickers use `Color`. `ui.Set(path, ui.Get(path))` preserves the solid color but not the picker's rainbow or pulse mode.
