Interface
Tabs, panels, components, and what actions return.
On this page
The ui block describes the Station tab of the application: a strip of tabs, each holding panels.
ui:
tabs:
- id: mods
name: Mods
panels:
- id: mod_list
type: table
title: Installed mods
source: {action: list_mods}
columns:
- {key: name, label: Mod}
- {key: version, label: Version, align: right}
row_actions:
- {label: Remove, action: remove_mod, tone: err, confirm: "Remove {{name}}?"}
empty: "No mods installed yet."
refresh: {seconds: 30}The script never returns HTML. It returns data, and Quasar draws it with its own components. This keeps stations safe, and readable with every theme.
| Field | Required | Description |
|---|---|---|
id |
yes | Lowercase letters, digits, - and _. |
name |
yes | Label in the tab strip. |
panels |
yes | At least one panel. |
Every panel accepts:
| Field | Description |
|---|---|
id |
Required. Unique across the station. Used to refresh the panel. Lowercase letters, digits, - and _. |
type |
Required. One of the components below. |
title |
Heading. |
help |
A line of help. |
width |
full (default), half or third. |
variant |
hero, plain or inset. |
tone |
accent, ok, warn or err. |
refresh |
{seconds: N}: reload the panel on a timer. |
source |
Where the data comes from. See below. |
| Source | Description |
|---|---|
{action: name} |
Calls the script’s name action and draws what it returns. |
{static: ...} |
Content written in the document. Nothing runs. |
{series: [a, b]} |
Recorded series. Only for chart. |
| Type | Description |
|---|---|
section |
Groups panels under a title. Takes panels. |
grid |
Lays panels out in a grid. Takes panels. |
divider |
A horizontal rule. |
banner |
A message across the tab. Usually source: {static: "..."}. |
Each data panel expects a specific shape in data:
| Type | Expected data | Example |
|---|---|---|
stat |
A value, or {value, suffix, label} |
{value: 3, suffix: "/ 20"} |
gauge |
Same as stat, value as a percentage |
{value: 72, suffix: "%"} |
keyvalue |
A list of {key, value}, or an object |
[{key: "Version", value: "1.21"}] |
table |
A list of rows (objects) | [{name: "Sodium", version: "0.5"}] |
list |
A list of strings or {label, note} |
["first", "second"] |
timeline |
Same as list |
[{label: "Restarted", note: "12:04"}] |
markdown |
Text, rendered as Markdown | "**Ready**" |
code |
Text, shown as code | "key=value" |
image |
An https:// URL or a data:image/... URI |
"data:image/svg+xml,..." |
chart |
Drawn from series, see charts | — |
log |
Streams a container’s logs, see below | — |
| Type | Description |
|---|---|
form |
Fields and a submit button. |
button |
One button: label and action. |
confirm |
A button that asks first: label, action and confirm (the question). |
search |
A search box: action and placeholder. The action receives {q}. |
button and search also accept long: true to run as a long action.
| Type | Description |
|---|---|
iframe |
Embeds a page. See below. |
- id: players
type: table
source: {action: list_players}
columns:
- {key: name, label: Player}
- {key: role, label: Role, type: badge}
row_actions:
- {label: Kick, action: kick, tone: err, confirm: "Kick {{name}}?"}
empty: "Nobody online."- Columns:
key(field of the row),label,align(left,right,center),type(text,badge,code). - Badge cells: a string, or
{label, tone}. An empty value shows no badge. - Row actions:
label,action,tone,confirm,long. The action receives the row’s fields.{{field}}inconfirmis replaced with the row’s value.
- id: settings
type: form
source: {action: read_settings}
fields:
- {name: motd, label: Message of the day}
- {name: difficulty, label: Difficulty, type: select, options: [easy, normal, hard]}
- {name: pvp, label: PvP, type: toggle}
submit: {label: Save, action: write_settings}- Field types:
text(default),number,select,toggle,secret,port,textarea,file. - Field options:
name(required),label,default,options,placeholder,help. - Submit:
label,action,tone,confirm,long. The action receives the values, keyed by field name.
The form’s source fills in the fields: return an object keyed by field name. For a select, a value can also carry its options:
return { data: { version: { value: '1.21.1', options: ['1.21.4', '1.21.1'] } } }- {id: console, type: log, title: Server output, service: minecraft, tail: 100}Streams the logs of a service live. Requires the logs permission for that service.
- id: map
type: iframe
src: "{{service:web:8123}}/"
height: 500pxsrc is either:
{{service:name:port}}followed by a path: a page served by one of the application’s containers. Quasar proxies it, so no port needs to be published. Requiresnet.internalfor that service and port.- an
https://address.
height defaults to 420px.
An action returns an object. Every key is optional:
| Key | Effect |
|---|---|
data |
Draws the panel that called the action. |
toast |
A success message (green). |
warn |
A warning message (orange): it worked, but not fully. |
error |
An error message (red). The action counts as failed. |
waiting |
Not ready yet: the panel shows a spinner and tries again shortly. |
refresh |
A list of panel ids to reload. |
navigate |
A tab id to switch to. |
download |
A file path in the app’s folder, offered for download. Must be covered by the files permission. |
export function remove_mod({ file }) {
quasar.files.delete(`data/mods/${file}`)
return { toast: 'Removed', refresh: ['mod_list'] }
}An action can also return the data directly, without {data: ...}.
Messages stack in a corner of the page. Success and warning messages disappear after a few seconds; errors stay until dismissed.
An action that takes a while (downloading many files, upgrading a server) should declare long: true on its button, row action or submit.
It then runs in the background, with a progress pane:
- report progress with
quasar.progress(percent, message); - the job survives a page reload;
- its output stays visible afterwards.
A download returned by a long action appears as a link in the progress pane.