Skip to main content
Your script’s own window in the menu. Declare pages and controls once, and the menu builds the widgets and saves their values into the user’s configs.
Declare at the top level, not in tick or render. There is no draw callback: you read control.value or listen for change. Everything is removed when the script unloads.

Overview

Layout

page, group, row.

Controls

switch, slider, combo, multi, color, input, button, text.

Reactive values

signal, computed, effect, batch.

Keybinds

key, mode, active, press / release.

Paths and persistence

id, persist, dispose.

Window

open, close, on.

Layout

page


A tab in the window. Asking for the same title (or id) again returns the same page, and the options of that repeat call are ignored. icon is a Tabler icon name; tabs sort by order (default 0). page.show() brings the tab to the front.

group


A panel. Same get-or-create rule as page. Without column, a page’s groups are split down the middle in declaration order.

row


Lays its children out side by side. build runs right away with the row.

Controls

Every container (page, group, row) has the same builders. The first argument is the label. A control has these members:

switch


A checkbox. key attaches a hotkey; true is an unbound "hold" bind.

slider


combo


One of items. The value is the item text; writing takes an item or its index. The items type the value, so combo("Mode", ["Legit", "Rage"]) is "Legit" | "Rage" without as const.
Items must be unique and non-empty. Writing an unknown item or index throws.

multi


Any number of items. The value is the picked items in list order; writing takes items and/or indices. Replacing items drops picks that are gone.

color


A color picker. The value is a new Color on every read; writing takes any ColorLike. Default white. key works like on a switch.

input


A text field.

button


Has no value. button.on("click", fn) subscribes, button.click() runs the listeners as a real click would.

text


A line of wrapped text. Pass a function to keep it up to date. Never saved.

Reactive values

label, visible, disabled and a text’s text accept a function, as an option or an assignment. It runs now and again whenever a control value, a key.active or a signal it read changes. Assigning again replaces it.
Exceptions thrown in reactive functions and handlers are reported through the global error event.

signal


A value holder. Setting value to something new fires change and re-runs whatever read it. Mutating an object in place doesn’t count.

computed


A read-only value derived from others. compute re-runs when something it read changes; change fires when the result differs.

effect


Runs fn now and again whenever anything it read changes, until the Subscription is disposed.

batch


Defers reactive re-runs until fn returns, then runs each affected function once. Returns what fn returns.

Keybinds

A switch, color or toggle group declared with key has a KeyBind at control.key. The user can rebind the key and change the mode in the menu; a persisted control saves both. The key option ({ key?, mode? }) is only a default: once the user binds or clears the key, theirs wins.

Paths and persistence

Saved values are keyed by path: the parent’s path plus the id option or the slugified label. ui.page("Aim").group("Main").slider("FOV") is aim/main/fov. Rows add nothing.
  • Two controls on the same path throw EEXIST. Give one an id (buttons and texts don’t clash).
  • Renaming a label moves the path and loses the saved value. Set id when the label may change.
  • persist: false keeps a value for the session only.

dispose


Removes a node and everything in it. After that alive is false, reads return null and writes throw. Declaring the same path again creates a fresh node. Signals and computeds have dispose() too.

Window


Example

A crosshair with its settings.