UI Controls
The complete ui.* control catalog — signatures, options, handle properties, and runnable examples
Op deze pagina
Every control is one ui.* call. Each call returns a handle (a chart returns a slightly different handle — see Charts). Reading and writing a handle's .value, and flipping .disabled, .hidden, or .label, updates the panel live at any time — not just while the run is happening.
Every control also accepts align: "start" | "center" | "end" in its options, placing it within the panel's width.
Reference table
| Control | Kind | Persists across Run |
|---|---|---|
ui.title | Text and structure | — |
ui.text | Text and structure | — |
ui.divider | Text and structure | — |
ui.card | Layout container | — |
ui.row | Layout container | — |
ui.tabs | Layout container | Yes (selected tab) |
ui.button | Input | — |
ui.slider | Input | Yes |
ui.number | Input | Yes |
ui.input | Input | Yes |
ui.toggle | Input | Yes |
ui.select | Input | Yes |
ui.color | Input | Yes |
ui.swatches | Input | Yes |
ui.readout | Output | No (script-driven) |
ui.stat | Output | No (script-driven) |
ui.progress | Output | No (script-driven) |
ui.chart | Output | No (script-driven) |
ui.table | Output | No (script-driven) |
ui.badge | Output | No (script-driven) |
ui.alert | Output | No (script-driven) |
ui.code | Output | No (script-driven) |
ui.spinner | Output | No (script-driven) |
ui.layout | Panel-wide | — |
"Persists across Run" means: when the panel is user-edited (a slider you dragged, text you typed), pressing Run to apply a code change keeps that value instead of resetting it — matched by the control's kind and label. Script-driven outputs always start from whatever your code sets them to on the new run.
Title, text, divider
ui.title("My App")
ui.text("A line of description.")
ui.text("Muted helper text.", { muted: true })
ui.divider()| Call | Options | Notes |
|---|---|---|
ui.title(text, opts?) | { align? } | A heading for the panel or a section. |
ui.text(text, opts?) | { muted?, align? } | A line of description; muted: true dims it. |
ui.divider(opts?) | { align? } | A horizontal rule to separate groups. |
Button
Runs onClick when pressed.
ui.button({
label: "Reset",
variant: "destructive",
size: "sm",
onClick: () => console.log("reset pressed"),
})ui.button({ label, onClick, variant?, size?, ...opts })
variant:"default" | "outline" | "secondary" | "destructive"size:"sm" | "default" | "lg"onClickmay beasync— a thrown error or a rejected promise is caught and reported to the Console.
Slider
Pick a number in a range.
const level = ui.slider({
label: "Level",
min: 0,
max: 255,
step: 1,
value: 128,
help: "Drag to set the level.",
onChange: (v) => console.log("level:", v),
})ui.slider({ label, min?, max?, step?, value?, help?, onChange?, ...opts }) — defaults: min: 0, max: 100, step: 1, value: 0.
Number
Type a number.
const count = ui.number({ label: "Count", value: 42, min: 0, max: 100, help: "0–100." })ui.number({ label, value?, min?, max?, step?, help?, onChange?, ...opts }) — value defaults to 0.
Input
Type text.
const name = ui.input({ label: "SSID", placeholder: "network name…", help: "Case-sensitive." })ui.input({ label, value?, placeholder?, help?, onChange?, ...opts }) — value defaults to "".
Toggle
An on/off switch.
const power = ui.toggle({ label: "Radio on", value: true, help: "Enable the radio." })ui.toggle({ label, value?, help?, onChange?, ...opts }) — value defaults to false.
Select
Choose one option from a list.
const mode = ui.select({ label: "Mode", options: ["Auto", "Manual", "Off"], value: "Auto" })ui.select({ label, options, value?, help?, onChange?, ...opts }) — value defaults to the first option (or "" if the list is empty).
Color
Pick a color.
const color = ui.color({ label: "Accent", value: "#4f46e5", help: "Any hex color." })ui.color({ label, value?, help?, onChange?, ...opts }) — value defaults to "#4f46e5". .value is always a "#rrggbb" string.
Swatches
Pick from a fixed palette instead of a full color picker.
const color = ui.color({ label: "Accent", value: "#0A84FF" })
const preset = ui.swatches({
label: "Presets",
colors: ["#0A84FF", "#34C759", "#FF3B30", "#BF5AF2", "#5AC8FA"],
value: "#0A84FF",
onPick: (hex) => { color.value = hex },
})ui.swatches({ colors, value?, label?, onPick?, onChange?, ...opts }) — .value is the selected hex. onPick fires with the hex string only when the user taps a swatch (in addition to the standard onChange).
Readout
A labeled value your app updates.
const op = ui.readout({ label: "Radio state", value: "idle" })
op.value = "scanning"ui.readout({ label, value?, ...opts })
Stat
A single figure, optionally with a unit.
const battery = ui.stat({ label: "Battery", unit: "%" })
battery.value = 87ui.stat({ label, value?, unit?, ...opts }) — value defaults to "—".
Progress
A progress bar toward a maximum.
const prog = ui.progress({ label: "Scanning", max: 100 })
prog.value = 40ui.progress({ label, value?, max?, ...opts }) — value defaults to 0, max to 100.
Chart
A live line chart you push values into over time. Its handle is shaped differently from every other control — no direct .value setter, but push/set/clear instead — because a chart's data is a series, not a single number.
const heap = ui.chart({ label: "Free heap", unit: "KiB", min: 0, max: 512 })
ui.every(1000, async () => {
const s = await device.rpc("state.read")
heap.push(Math.round((s.free_heap ?? 0) / 1024))
})ui.chart({ label, min?, max?, unit?, points?, ...opts }) — points seeds initial data.
| Method | Does |
|---|---|
.push(v) | Append one value, keeping the most recent 120 points. |
.set(values) | Replace the whole series (also capped to the most recent 120). |
.clear() | Empty the series. |
.disabled / .hidden / .label | Same as every other handle. |
Table
Rows and columns of results.
ui.table({
columns: ["MAC", "RSSI", "Name"],
rows: [
["AA:BB:CC:11:22:33", -42, "Pixel 8"],
["11:22:33:AA:BB:CC", -67, "unknown"],
],
})ui.table({ columns, rows, ...opts }) — rows is an array of arrays; short rows are padded and long rows are trimmed to match columns.
Badge
A small status label.
ui.badge({ label: "connected", variant: "secondary" })ui.badge({ label, variant?, ...opts }) — variant: "default" | "secondary" | "outline" | "destructive".
Alert
A callout message in the panel.
ui.alert({ title: "Heads up", description: "This will erase saved presets.", variant: "warning" })ui.alert({ title, description?, variant?, ...opts }) — variant: "default" | "info" | "success" | "warning" | "destructive".
Code
A block of preformatted text or code.
ui.code({ text: JSON.stringify({ ok: true }, null, 2), language: "json" })ui.code({ text, language?, ...opts })
Spinner
A busy indicator while something is running.
const spin = ui.spinner({ label: "Scanning…" })
// spin.hidden = true once the work is doneui.spinner({ label?, ...opts })
Card
A titled box that groups other controls together.
ui.card({ title: "Radio", description: "Sub-GHz settings" }, () => {
ui.toggle({ label: "Enabled" })
ui.slider({ label: "Frequency (MHz)", min: 300, max: 928 })
})ui.card({ title?, description?, ...opts }, children)
Row
Lays child controls out side by side. Form fields (input/select/slider/number/color) stretch to share the width; buttons and badges keep their natural size.
const level = ui.slider({ label: "Level", max: 100 })
const reset = ui.button({ label: "Reset", onClick: () => { level.value = 0 } })
ui.row([level, reset], { justify: "between" })ui.row(children, { justify?, wrap?, ...opts }) — justify: "start" | "center" | "end" | "between".
Tabs
Organizes controls into switchable tabs. The selected tab index persists across a Run, like a value control.
ui.tabs({
tabs: [
{ label: "Basic", children: () => { ui.text("Basic settings") } },
{ label: "Advanced", children: () => { ui.text("Advanced settings") } },
],
})ui.tabs({ tabs: [{ label, children }], ...opts })
Both children forms
Every container (card, row, tabs) accepts children either as an array of handles you already created, or as a function that declares them inline. Both are natural depending on whether you need to reference a control later:
// Array form — you already have the handles.
const a = ui.button({ label: "A", onClick: () => {} })
const b = ui.button({ label: "B", onClick: () => {} })
ui.row([a, b])
// Function form — declare inline.
ui.row(() => {
ui.button({ label: "A", onClick: () => {} })
ui.button({ label: "B", onClick: () => {} })
})Panel layout (ui.layout)
Not a control — sets options for the whole panel, not one control. Call it once, anywhere in your script.
ui.layout({ align: "center", maxWidth: 460 })
// or, to use the full available width:
ui.layout({ maxWidth: "full" })ui.layout({ align?, maxWidth? })
align:"start" | "center" | "end"— the default alignment for controls that don't set their own.maxWidth: a pixel width for the panel, or"full"to remove the cap.