- Every control is a signal.
fov.valueis tracked, so a control can feedcomputed,effector another control’s prop directly:visible: enabled. - Any prop can be reactive. Give
label,visible,items,min,tooltipand friends a value, a function, or a signal / control / hotkey, and it follows.
tick or render. Everything goes away when the script unloads.
Overview
Layout
page, group, section, row, stack, each.Controls
switch, slider, range, combo, multi, selector, list, color, input, bind.Everything else
button, text, separator, image, canvas, menus, tooltips.Reactivity
signal, computed, effect, batch, untrack.Hotkeys
key, mode, active, press / release.Window and dialogs
open, confirm, prompt, snapshot, JSX.Layout
page
id) again returns the same page; build runs every time.
icon is a Tabler icon name, tabs sort by order. page.show() brings it to the front.
group
column, a page’s groups split down the middle.
section
section.expanded is readable, writable and reactive.
row, stack
each
render declares an item’s nodes on the same container (they land in the
block); items that leave take their nodes with them, new ones render, moved ones move. A string or
number key also scopes the item’s paths, so per-item values save.
Controls
Every container has the same builders. The first argument is the label.
A control has:
switch
slider, range
precision: 0 (the default) makes an integer slider. min, max and unit are reactive; the
value follows a narrowed range. A range has two handles and a [low, high] value.
combo, multi, selector
combo("Mode", ["Legit", "Rage"]) is "Legit" | "Rage".
items is reactive. A selector draws the same thing as chips.
list
color, input, bind
bind is a hotkey on a row of its own; its
value is whether it’s active, so visible: panic works.
Buttons, text, images
button.click() runs the listeners like a real click. A text never saves.
canvas
g is render’s API in canvas-local pixels. It
redraws when something draw read changes, or every tick with animate: true. info has
width, height, hovered, pressed, pointer and time, all tracked.
Pointer input comes as events: pointerdown, pointerup, pointermove, pointerenter,
pointerleave and wheel, with x, y, button and deltaY.
menus
Any node takes a right-click menu: items withlabel, icon, danger, disabled, onClick
and items for a submenu, and "-" for a separator. Assign null to remove it.
Reactivity
A computed, effect or reactive prop re-runs when avalue it read changes: a control’s, a
signal’s, a hotkey’s active, a section’s expanded, ui.isOpen. It’s glitch-free: one re-run
per change, never a half-updated state.
signal.subscribe((value, previous) => ...) listens without an effect. Exceptions in handlers
and reactive functions go to the global error event.
Hotkeys
A switch, color or toggle group declared withkey has a KeyBind at control.key.
The
key option (true, a key code, a mode or { 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 theid option or the slugified label.
ui.page("Aim").group("Main").slider("FOV") is aim/main/fov. Rows, stacks and sections add
nothing.
- Two controls on one path throw
EEXIST. Give one anid. - Renaming a label moves the path and loses the saved value. Set
idwhen the label may change. persist: falsekeeps a value for the session only.- After
dispose(), reads and writes throwESTALE;aliveandidstill answer.
Window and dialogs
Dialogs are the menu’s own modal, as promises. Closing one any other way than OK is a no.
JSX
In a.tsx file (the template’s tsconfig already sets "jsxImportSource": "@native/ui") the
same tree reads like markup. It’s the same builders underneath.
model binds a control to a signal both ways, ref hands you the node, <For each> is each().